【问题标题】:File download from PC to Android device - OutputStream write( ) stucks for large files文件从 PC 下载到 Android 设备 - OutputStream write() 卡住大文件
【发布时间】:2014-03-30 16:16:03
【问题描述】:

我正在尝试将文件从 PC 下载到 Android 设备(模拟器或物理设备)

中小型文件一切正常,但是当我尝试发送大于 10Mb 的文件时,服务器永远不会到达此语句 out2.println("ready");,因此客户端挂起等待来自服务器的 "ready"
似乎服务器卡在这个:os.write(bytearray, 0, bytearray.length);

到目前为止,我已经在模拟器上尝试过了,我将很快报告真实设备上的结果

这是我的客户端部分,在 Android 应用程序中:Android 客户端向服务器发送“下载”字符串,然后从服务器接收“就绪”回复,并开始从 InputStream 读取文件

int filesize = 2022386;
int bytesRead;
int currentTot = 0;

Socket socket1 = new Socket("172.16.6.119", 50001); //data line
Socket socket2 = new Socket("172.16.6.119", 50001); //control line

BufferedReader in2 = new BufferedReader(new InputStreamReader(socket2.getInputStream()));  
PrintWriter out2 = new PrintWriter(socket2.getOutputStream(), true);

out2.println("download:"); //control line

String usrtxt = in2.readLine();

if(usrtxt.substring(0,5).equals("ready")) //control line
                {
                    byte [] bytearray = new byte [filesize];

                    InputStream is = socket1.getInputStream(); //data line
                    FileOutputStream fos = new FileOutputStream(Environment.getExternalStorageDirectory().toString() + "/sunset.jpg");
                    BufferedOutputStream bos = new BufferedOutputStream(fos);
                    bytesRead = is.read(bytearray,0,bytearray.length);
                    currentTot = bytesRead;

                    Log.e("DOWNLOAD", "init value: bytesread = " + Integer.toString(bytesRead));

                    do {
                    bytesRead =
                    is.read(bytearray, currentTot, (bytearray.length-currentTot)); //<--does not pull data (WHY?)

                    Log.e("DOWNLOAD", "bytesread = " + Integer.toString(bytesRead));

                    if(bytesRead >= 0) currentTot += bytesRead;
                    } while(bytesRead > -1);

                    out1.println("finished:");

                    bos.write(bytearray, 0 , currentTot);

                    Log.e("DOWNLOAD", Integer.toString(currentTot));
                    bos.flush();
                    bos.close();
                    socket1.close(); socket2.close();       

                }

这是我的服务器代码:它接受来自客户端的连接,然后从客户端接收“下载”字符串,回复“就绪”,然后将文件放入 OutputStream

while (true) {

                final Socket socket = serverSocket.accept();  //data line
                final Socket socket2= serverSocket.accept();  //control line          

                BufferedReader in2 = new BufferedReader(new InputStreamReader(socket2.getInputStream()));  
                PrintWriter out2 = new PrintWriter(socket2.getOutputStream(), true);

                String usrtxt = in2.readLine(); //control line

                if(usrtxt.substring(0,8).equals("download"))
                {
                System.out.println("accepted download request. sending file");  

                File transferFile = new File("sunset.jpg"); 

                byte[] bytearray = new byte[(int)transferFile.length()];
                FileInputStream fin = new FileInputStream(transferFile);
                BufferedInputStream bin = new BufferedInputStream(fin);
                bin.read(bytearray, 0, bytearray.length);

                OutputStream os = socket.getOutputStream();
                os.write(bytearray, 0, bytearray.length); //data line


                os.flush();
                os.close();

                out2.println("ready"); //control line


                socket.close(); socket2.close();
                bin.close(); fin.close();


                System.out.println("file transfer complete");

                }//end if           

            }//end while

【问题讨论】:

  • 您正在从套接字中读取一次,并期望它一次读取全部内容。这是极不可能的。 read 仅读取已传输且当前可用的任何数据。
  • @BrianRoach 但我正在使用一个循环,该循环读取直到没有任何东西可以获取。
  • 只有当服务器关闭连接时它才会知道“没有什么可以获取”——直到客户端说它已经完成它才会这样做。僵局!阅读marcgravell.blogspot.co.uk/2013/02/…
  • @JonSkeet 但服务器如何知道何时关闭?
  • 您的循环包括接受连接。它为每个与其建立的 TCP 连接只循环一次,然后在 socket 超出范围时将该连接转储到循环底部。

标签: java android sockets download inputstream


【解决方案1】:

这不是复制流的方法。它假设了太多可能不正确的事情。

Java中的规范方式如下:

byte[] buffer = new byte[8192];  // or whatever you like, anything above zero. Note that it doesn't have to be the size of the file
int count;
while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

如果您事先知道长度并且想在之后保持套接字打开,请通过“total += count;”跟踪到目前为止传输的字节数;在循环内的读取调用之后,将读取调用更改为read(buffer, 0, length-total &gt; buffer.length ? buffer.length : (int)(length-total))

【讨论】:

  • 不,我不需要保持套接字打开。但我真的不明白这与一次将整个文件放到流中有何不同。无论如何,客户端将不会开始下载,直到服务器通过将 -1 放入它来关闭其 OutputStream
  • 也许你的意思是我应该读取一些部分,然后刷新并关闭 OutputStream,然后将ready 发送到客户端,然后再次读取一些部分,依此类推,直到读取完所有字节?
  • 好的,请澄清sent 的意思。在我的理解中,一个字节被发送,当服务器把它放到OutputStream上,关闭OutputStream,客户端获取它的InputStream,直到它收到-1
  • 这些都不是真的。它不仅不同而且更好,因为它可以工作,不会增加延迟,不会假设文件适合内存,或者它的大小适合'int',......接收器可以立即读取一个字节已发送一个字节。我的意思不比我在这里写的更多。您可以在两端使用此代码,并使用合适的 'in' 和 'out' 值,并且应该这样做,而不是现在。
  • 不,当发送者写入时发送一个字节。如果发送者正在使用缓冲流,它可能会被推迟到他刷新,或者它自己刷新。
猜你喜欢
  • 2014-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
  • 1970-01-01
相关资源
最近更新 更多