我已经使用插座制作了一个基本的客户端服务器FTP程序,但是由于某些原因,文件在传输过程中被损坏。在下面的情况下,我将文件从客户端推向服务器。它几乎有效,因为某些文件(例如.png)传输并打开罚款,但其他文件(.docx)却没有。我传输的任何文件与我发送的文件不同。
客户端代码:
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();
}
}
服务器代码:
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类:
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
}
我敢肯定,这是我在这里缺少的很小的东西。有什么想法吗?
这发生了,因为服务器正在编写整个1024个字节块,即使实际写在块上的1024个字节少于1024个字节。
解决方案(感谢@kdgregory)是使用FileInputStream.read()
的返回值来填充我的ftpdatablock类中的新属性,int bytesWritten
。
然后在服务器端我可以使用:
FileOutputStream.write(currentBlock.getData(), 0, currentBlock.getBytesWritten());
要将确切的字节数写入文件,而不是每次都要整个块。
我认为文件扩展程序可能存在问题。在客户端提供选项:
FILE_TO_RECEIVED = JOptionPane.showInputDialog("Please enter the Drive followed by the file name to be saved. Eg: D:/xyz.jpg");
它是为了帮助您提供正确的文件扩展名称。
那么我认为您还应该在客户端提供文件大小,例如:
public final static int FILE_SIZE = 6022386;
,然后在阵列块中,您可以进行以下更改为:
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();
}