2013-06-24 131 views
2

我寫了一個用於傳輸小文件的小型客戶機 - 服務器代碼。它使用數據輸入流的數據輸出流和readFully()方法。出於顯而易見的原因,此代碼不適用於較大的文件。我正在考慮在將大文件發送到客戶端之前將它們分成更小的1Kb塊。但是我不能想到任何解決方案(比如如何在數據輸出流上寫入多個塊,並使用正確的偏移量以及如何在接收端重新組合它們。任何人都可以提供解決方法嗎?如果您可以修改代碼,這將非常有幫助:通過java套接字進行大文件傳輸

發件人(服務器):

public void sendFileDOS() throws FileNotFoundException { 
    runOnUiThread(new Runnable() { 
      @Override 
      public void run() { 
       registerLog("Sending. . . Please wait. . ."); 
      } 
     }); 
    final long startTime = System.currentTimeMillis(); 
    final File myFile= new File(filePath); //sdcard/DCIM.JPG 
    byte[] mybytearray = new byte[(int) myFile.length()]; 
    FileInputStream fis = new FileInputStream(myFile); 
    BufferedInputStream bis = new BufferedInputStream(fis); 
    DataInputStream dis = new DataInputStream(bis); 
    try { 
     dis.readFully(mybytearray, 0, mybytearray.length); 
     OutputStream os = socket.getOutputStream(); 
     //Sending file name and file size to the client 
     DataOutputStream dos = new DataOutputStream(os);  
     dos.writeUTF(myFile.getName());  
     dos.writeLong(mybytearray.length);  
     int i = 0; 
     final ProgressBar myProgBar=(ProgressBar)findViewById(R.id.progress_bar); 
     while (i<100) { 
      dos.write(mybytearray, i*(mybytearray.length/100), mybytearray.length/100); 
      final int c=i; 
      runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 
         myProgBar.setVisibility(View.VISIBLE); 
         registerLog("Completed: "+c+"%"); 
         myProgBar.setProgress(c); 
         if (c==99) 
          myProgBar.setVisibility(View.INVISIBLE); 
        } 
       }); 
      i++; 
     }  
     dos.flush(); 

    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
    runOnUiThread(new Runnable() { 
      @Override 
      public void run() { 
       long estimatedTime = (System.currentTimeMillis() - startTime)/1000; 
       registerLog("File successfully sent"); 
       registerLog("File size: "+myFile.length()/1000+" KBytes"); 
       registerLog("Elapsed time: "+estimatedTime+" sec. (approx)"); 
       registerLog("Server stopped. Please restart for another session."); 
       final Button startServerButton=(Button)findViewById(R.id.button1); 
       startServerButton.setText("Restart file server"); 
      } 
     }); 
} 

接收器(客戶端):!

public class myFileClient { 
final static String servAdd="10.141.21.145"; 
static String filename=null; 
static Socket socket = null; 
static Boolean flag=true; 

/** 
* @param args 
*/ 
public static void main(String[] args) throws IOException { 
    // TODO Auto-generated method stub 
    initializeClient(); 
    receiveDOS();  
} 
public static void initializeClient() throws IOException { 
    InetAddress serverIP=InetAddress.getByName(servAdd); 
    socket=new Socket(serverIP, 4444); 
} 
public static void receiveDOS() { 
    int bytesRead; 
    InputStream in; 
    int bufferSize=0; 

    try { 
     bufferSize=socket.getReceiveBufferSize(); 
     in=socket.getInputStream(); 
     DataInputStream clientData = new DataInputStream(in); 
     String fileName = clientData.readUTF(); 
     System.out.println(fileName); 
     OutputStream output = new FileOutputStream("//home//evinish//Documents//Android//Received files//"+ fileName); 
     long size = clientData.readLong(); 
     byte[] buffer = new byte[bufferSize]; 
     while (size > 0 
       && (bytesRead = clientData.read(buffer, 0, 
         (int) Math.min(buffer.length, size))) != -1) { 
      output.write(buffer, 0, bytesRead); 
      size -= bytesRead; 
     } 

    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 

請幫助在此先感謝:)

+0

看這裏http://stackoverflow.com/questions/5113914/large-file-transfer-with-sockets?rq=1 – XXX

回答

4

你鑽機ht,這是一個很糟糕的方式來做到這一點。它浪費了記憶和時間;它假定文件大小是32位;它假定整個文件適合內存;它假定整個文件在一次讀取中被讀取;並且在整個文件被讀取之前它不發送任何東西。

的規範辦法複製Java中的數據流是這樣的:

while ((count = in.read(buffer)) > 0) 
{ 
    out.write(buffer, 0, count); 
} 

它將與你喜歡的,因此與任何大小的文件,你可以拿出任何大小的緩衝區工作。兩端使用相同的代碼,但不必在兩端使用相同大小的緩衝區。當您通過網絡複製時,您可能會認爲1k或1.5k是最佳大小,但是忽略了內核中套接字發送和接收緩衝區的存在。當你考慮到它時,最好使用8k或更多。

+0

Thanx!讓我重寫我的代碼!將結果返回給您。 :) –

+0

::我試過上述方法,但是,收到的文件不完整。我不知道如何在接收器上重新組裝數據包。您可以根據您的建議修改發件人和收件人代碼嗎?我的頭腦完全被堵塞,似乎沒有任何工作! :( –

+0

你不必在接收端重新組裝數據包,只需將它們寫入你的文件,兩端使用相同的代碼我已經說過了 – EJP

1

我終於解決了這個問題。這裏是我修改的服務器和客戶端源代碼。希望這可以幫助其他人! :) 服務器端代碼段(發送者):

final File myFile= new File(filePath); //sdcard/DCIM.JPG 
    byte[] mybytearray = new byte[8192]; 
    FileInputStream fis = new FileInputStream(myFile); 
    BufferedInputStream bis = new BufferedInputStream(fis); 
    DataInputStream dis = new DataInputStream(bis); 
    OutputStream os; 
    try { 
     os = socket.getOutputStream(); 
     DataOutputStream dos = new DataOutputStream(os); 
     dos.writeUTF(myFile.getName());  
     dos.writeLong(mybytearray.length); 
     int read; 
     while((read = dis.read(mybytearray)) != -1){ 
      dos.write(mybytearray, 0, read); 
     } 

    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

客戶端側的代碼段(接收器):

long acc=0; 
long N=myFile.length(); 
while(acc<N){ 
      noofbytes=dis.read(mybytearray, 0, 16384); 
      dos.write(mybytearray, 0, noofbytes); 
      acc=acc+noofbytes; } dos.flush(); 

:寫入輸出流的

int bytesRead; 
    InputStream in; 
    int bufferSize=0; 

    try { 
     bufferSize=socket.getReceiveBufferSize(); 
     in=socket.getInputStream(); 
     DataInputStream clientData = new DataInputStream(in); 
     String fileName = clientData.readUTF(); 
     System.out.println(fileName); 
     OutputStream output = new FileOutputStream("//home//evinish//Documents//Android//Received files//"+ fileName); 
     byte[] buffer = new byte[bufferSize]; 
     int read; 
     while((read = clientData.read(buffer)) != -1){ 
      output.write(buffer, 0, read); 
     } 

    } catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 

的位更快的方式傳輸72MB的視頻文件時,我保存了大約7秒。

+0

這段代碼不起作用你是毫無意義的,使用writeLong()寫入緩衝區大小,但從不讀取它,所以你正在向文件中寫入額外的8個字節,而不需要加倍/ /,其餘部分與我的答案基本相同,沒有確認 – EJP

+0

嗯,你是對的,我寫的文件大小毫無意義,我已經包括了測試目的,忘記刪除它,但對於其他代碼,它工作得很好。事實上,我已經設計了一種新的寫入輸出流的解決方案,它有點快。你可以參考編輯部分。 –

+0

你在做夢。幾周前我給你工作代碼。您可以通過使用更大的緩衝區來提高速度。您的修改無效。限制讀取到16384字節不會讓它更快。如果可以的話,你應該讓它填充輸入緩衝區。不測試-1是肯定不安全的:您有可能使用該代碼獲取'ArrayIndexOutOfBoundsException',並且獲取'acc'的錯誤值,這將導致'while'條件發生故障。你只是變得更糟。你還沒有修復'//'s。 – EJP