1
votes

I've made a basic client server FTP program using sockets, but for some reason files are getting corrupted during the transfer. In the case below, I'm pushing a file to the server from the client. It almost works, since some files (such as a .png) transfer and open fine, but others (a .docx) don't. Any file that I transfer has a different MD5 to the one I sent.

Client code:

File file = null;
            FTPDataBlock transferBlock;
            int numBytesRead = 0;
            int blockNumber = 1;
            int blockSize = 1024;
            byte[] block = new byte[blockSize];
            fc = new JFileChooser();

            // select file to upload
            int returnVal = fc.showOpenDialog(Client.this);
            if (returnVal == JFileChooser.APPROVE_OPTION) {
                file = fc.getSelectedFile();

                try {
                    // get total number of blocks and send to server
                    int totalNumBlocks = (int)Math.ceil((file.length()*1.0) / blockSize);
                    System.out.println("File length is: " + file.length());
                    FTPCommand c = new FTPCommand("PUSH", Integer.toString(totalNumBlocks));
                    oos = new ObjectOutputStream(sock.getOutputStream());
                    oos.writeObject(c);
                    oos.flush();

                    // send to server block by block
                    FileInputStream fin = new FileInputStream(file);
                    while ((numBytesRead = fin.read(block)) != -1){
                        transferBlock = new FTPDataBlock(file.getName(), blockNumber, block);
                        blockNumber++;
                        System.out.println("Sending block " + transferBlock.getBlockNumber() + " of " + totalNumBlocks);
                        oos = new ObjectOutputStream(sock.getOutputStream());
                        oos.writeObject(transferBlock);
                        oos.flush();
                    }

                    fin.close();
                    System.out.println("PUSH Complete");

                    // get response from server
                    ois = new ObjectInputStream(sock.getInputStream());
                    FTPResponse response = (FTPResponse)ois.readObject();
                    statusArea.setText(response.getResponse());


                } catch (IOException | ClassNotFoundException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }

Server Code:

else if (cmd.getCommand().equals("PUSH")){
                    // get total number of file blocks
                    int totalNumBlocks = Integer.parseInt(cmd.getParameters());

                    // get first block
                    in = new ObjectInputStream(sock.getInputStream());
                    FTPDataBlock currentBlock = (FTPDataBlock)in.readObject();

                    // create file and write first block to file
                    File file = new File (workingDirectory + File.separator + currentBlock.getFilename());
                    FileOutputStream fOut = new FileOutputStream(file);
                    fOut.write(currentBlock.getData());
                    fOut.flush();

                    // get remaining blocks
                    while(currentBlock.getBlockNumber()+1 <= totalNumBlocks){
                        in = new ObjectInputStream(sock.getInputStream());
                        currentBlock = (FTPDataBlock)in.readObject();
                        fOut.write(currentBlock.getData());
                        fOut.flush();
                    }

                    fOut.close();

                    // send response
                    FTPResponse response = new FTPResponse("File Received OK");
                    out = new ObjectOutputStream(sock.getOutputStream());
                    out.writeObject(response);
                }

FTPDataBlock class:

public class FTPDataBlock implements Serializable{

    private static final long serialVersionUID = 1L;
    private String filename;
    private int blockNumber; // current block number
    private byte[] data;
    //constructors & accessors
}

I'm sure it's something small that I'm missing here. Any ideas?

2
I would suggest to implement unit test to find bug in this codejbaliuka
Maybe you have a problem with your blocks. If you send a file whose size is N*1024 bytes, is it corrupted? What does FTPCommand do with the block number?StephaneM
(1) Reread the documentation for InputStream.read(byte[]), in particular what the possible return values might be. (2) Compare the sizes of your input file and your output file. (3) Figure out how #1 and #2 are related.kdgregory
Also, don't create multiple ObjectXStreams, Use the one you already have. And use writeUnshared() rather than writeObject().kdgregory
Here is the part of the documentation that you're not reading: The number of bytes actually read is returned as an integer. Hint: it may be smaller than the size of your buffer.kdgregory

2 Answers

1
votes

This happened because the server was writing whole 1024 byte blocks to the file, even if there was less than 1024 bytes actually written to the block.

The solution (thanks to @kdgregory) was to use the return value of FileInputStream.read() to populate a new attribute in my FTPDataBlock class, int bytesWritten.

Then on the server side I could use:

FileOutputStream.write(currentBlock.getData(), 0, currentBlock.getBytesWritten());

to write the exact number of bytes to the file, instead of the whole block every time.

-1
votes

I think there may be a problem with the file extension. provide a option in the client side as:

FILE_TO_RECEIVED = JOptionPane.showInputDialog("Please enter the Drive followed                                  by the file name to be saved. Eg: D:/xyz.jpg");  

it is to help you to provide the correct file extension name.

then i think u should also provide the file size in client side like:

   public final static int FILE_SIZE = 6022386; 

and in then in the array block u used u can make the following changes as:

 try {
  sock = new Socket(SERVER, SOCKET_PORT);

  byte [] mybytearray  = new byte [FILE_SIZE];
  InputStream is = sock.getInputStream();
  fos = new FileOutputStream(FILE_TO_RECEIVED);
  bos = new BufferedOutputStream(fos);
  bytesRead = is.read(mybytearray,0,mybytearray.length);
  current = bytesRead;

  do {
     bytesRead =
        is.read(mybytearray, current, (mybytearray.length-current));
     if(bytesRead >= 0) current += bytesRead;
  } while(bytesRead > -1);

  bos.write(mybytearray, 0 , current);
  bos.flush();

}