2013-06-24 130 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