[英]FileInputStream and DataOutputStream - handling byte[] buffer
我一直在研究一个应用程序,在两个主机之间移动文件,同时我让传输过程工作(代码仍然非常混乱,很抱歉,我还在修复它)我有点想知道它到底是怎么回事处理缓冲区。 我对java中的网络相当陌生,所以我只是不想最终得到“嗯,我让它工作,所以让我们继续前进”的态度。
文件发送代码。
public void sendFile(String filepath, DataOutputStream dos) throws Exception{
if (new File(filepath).isFile()&&dos!=null){
long size = new File(filepath).length();
String strsize = Long.toString(size) +"\n";
//System.out.println("File size in bytes: " + strsize);
outToClient.writeBytes(strsize);
FileInputStream fis = new FileInputStream(filepath);
byte[] filebuffer = new byte[8192];
while(fis.read(filebuffer) > 0){
dos.write(filebuffer);
dos.flush();
}
文件接收代码
public void saveFile() throws Exception{
String size = inFromServer.readLine();
long longsize = Long.parseLong(size);
//System.out.println(longsize);
String tmppath = currentpath + "\\" + tmpdownloadname;
DataInputStream dis = new DataInputStream(clientSocket.getInputStream());
FileOutputStream fos = new FileOutputStream(tmppath);
byte[] filebuffer = new byte[8192];
int read = 0;
int remaining = (int)longsize;
while((read = dis.read(filebuffer, 0, Math.min(filebuffer.length, remaining))) > 0){
//System.out.println(Math.min(filebuffer.length, remaining));
//System.out.println(read);
//System.out.println(remaining);
remaining -= read;
fos.write(filebuffer,0, read);
}
}
我想知道如何处理双方的缓冲区以避免写错字节。 (ik如何接收代码避免这种情况,但我仍然想知道如何处理字节数组)
fis / dis总是等待缓冲区完全填满吗? 在接收代码时,如果它小于filebuffer.length,它总是写入完整数组或剩余长度但是发送代码时fis怎么样。
事实上,您的代码可能有一个微妙的错误,完全是因为您处理缓冲区的方式。
从原始文件读取缓冲区时, read(byte[])
方法返回实际读取的字节数。 实际上,无法保证已读取所有8192个字节。
假设您有一个10000字节的文件。 您的第一个读操作读取8192个字节。 但是,第二次读操作只能读取1808个字节。 第三个操作将返回-1。
在第一次读取时,您准确写入已读取的字节,因为您读取了一个完整的缓冲区。 但在第二次读取时,您的缓冲区实际上包含1808个正确的字节,剩余的6384个字节是错误的 - 它们仍然存在于上一次读取中。
在这种情况下,您很幸运,因为这只发生在您编写的最后一个缓冲区中。 因此,当您达到预先发送的长度时,您在客户端停止读取这一事实会导致您跳过那些您不应该发送的错误字节。
但事实上,即使还没有达到目的,也没有实际保证从文件读取将返回8192个字节。 该方法的合同不保证,它取决于操作系统和底层文件系统。 例如,它可以在您第一次读取时向您发送5000个字节,在第二次读取时向您发送5000个字节。 在这种情况下,您将在文件中间发送3192错误的字节。
因此,您的代码应该看起来像:
byte[] filebuffer = new byte[8192];
int read = 0;
while(( read = fis.read(filebuffer)) > 0){
dos.write(filebuffer,0,read);
dos.flush();
}
很像你在接收方的代码。 这可以保证只写入读取的实际字节数。
因此,缓冲区的处理方式实际上并不神奇。 你给流一个缓冲区,你告诉它允许填充多少缓冲区,但不能保证它会填满所有缓冲区。 它可能填充较少,你必须小心,只使用它告诉你它填充的部分。
但是,您正在犯的另一个严重错误是将您收到的long
转换为此行中的int
:
int remaining = (int)longsize;
文件可能比整数包含的长。 尤其是像东西长视频等,这就是为什么你得到这个数字的long
摆在首位。 不要那样截断它。 保持remaining
long
并且只有在取得最小值后才将其更改为int
(因为您知道最小值将始终在int
的范围内)。
long remaining = longsize;
long fileBufferLen = filebuffer.length;
while((read = dis.read(filebuffer, 0, (int)Math.min(fileBufferLen, remaining))) > 0){
...
}
顺便说一下,没有真正的理由为此使用DataOutputStream
和DataInputStream
。 read(byte[])
, read(byte[],int,int)
, write(byte[])
和write(byte[],int,int)
继承自底层InputStream
,没有理由不直接使用套接字的OutputStream
/ InputStream
,或使用BufferedOutputStream
/ BufferedOutputStream
来包装它。 在完成写入/阅读之前,也无需使用flush
。
此外,完成后,不要忘记至少关闭文件输入/输出流。 您可能希望打开套接字输入/输出流以继续通信,但不需要保持文件本身打开,这可能会导致问题。 使用try-with-resources确保它们已关闭。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.