[英]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.