6

我编写了一个用于传输小文件的小型客户端-服务器代码。它使用数据输出流和数据输入流的 readFully() 方法。由于明显的原因,此代码不适用于较大的文件。我正在考虑将大文件分成每个 1Kb 的小块,然后再将它们发送给客户端。但是我想不出任何解决方案(例如如何在具有正确偏移量的数据输出流上写入多个块以及如何在接收端重新组装它们。谁能提供解决方法?如果您可以修改我的代码将非常有帮助:

发件人(服务器):

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();
    }
}

请帮忙!提前致谢!:)

4

2 回答 2

6

你是对的,这是一个糟糕的方法。它既浪费内存又浪费时间;它假设文件大小为 32 位;它假设整个文件都适合内存;它假定一次读取整个文件;并且在读取整个文件之前它不会发送任何内容。

在 Java 中复制流的规范方法是:

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

它适用于您喜欢的任何大小的缓冲区,因此适用于您可以提出的任何大小的文件。在两端使用相同的代码,尽管您不必在两端使用相同大小的缓冲区。当您通过网络进行复制时,您可能认为 1k 或 1.5k 是最佳大小,但这忽略了内核中套接字发送和接收缓冲区的存在。当您考虑到它们时,使用 8k 或更多可能会更好。

于 2013-06-24T23:48:45.373 回答
5

我终于解决了这个问题。这是我修改后的服务器和客户端源代码。希望这对其他人也有帮助!:) 服务器端代码片段(发件人):

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();
    }

客户端代码片段(接收方):

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();
    }

一种更快的写入输出流的方法:

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();

我在传输 72MB 的视频文件时节省了大约 7 秒。

于 2013-06-26T12:21:38.097 回答