【问题标题】:How to monitor file upload progress?如何监控文件上传进度?
【发布时间】:2011-09-11 18:52:59
【问题描述】:

我需要将文件上传到服务器并监控它的进度。 我需要通知每次发送多少字节。

例如在下载的情况下我有:

HttpURLConnection  connection = (HttpURLConnection) m_url.openConnection();
connection.connect();
InputStream stream = connection.getInputStream();
while ((currentBytes  = stream.read(byteBuffer)) > 0) {                    
    totalBytes+=currentBytes;
    //calculate something...
}

现在我需要为上传做同样的事情。但如果使用

OutputStreamWriter stream = new OutputStreamWriter(connection.getOutputStream());
stream.write(str);
stream.flush();

我无法收到任何进度通知,关于发送了多少字节(看起来像是自动操作)。

有什么建议吗?

谢谢

【问题讨论】:

  • 很遗憾你没有得到这个问题的真正答案,我很好奇......
  • 似乎 HttpURLConnection 不是该任务的正确对象。它对输出是非阻塞的,所以我无法用它来衡量性能。我现在正在尝试使用套接字。
  • 是的,对于套接字,您可以使用 getSendBufferSize 来确定它们何时发送数据。
  • 它不是'非阻塞输出'。 @MirroredFate getSendBufferSize() 不能“确定它们何时发送数据”。它返回一个固定的值,除非您自己更改它。它不会随发送进度而变化。

标签: java post upload


【解决方案1】:

只需设置分块传输模式并监控您自己对输出流的写入。

如果您不使用分块或定长传输模式,您的写入在写入网络之前都会写入 ByteArrayOutputStream,以便 Java 可以正确设置 Content-Length 标头。这就是让您认为它是非阻塞的原因。不是。

【讨论】:

  • 确实 connection.setChunkedStreamingMode(2048); 成功了。
  • @btoueg '它是用于流式传输的,所以它不会按预期工作' 为​​什么?
  • 这取决于您的需求。如果你需要上传 2Mb~5Mb 的文件,那么流式传输是没有意义的。
  • @BenjaminToueg 当然是有道理的。它通过不写入 ByteArrayOutputStream 为您节省 2-5MB 的内存。您还没有说明为什么它“不能按预期工作”,并且您现在还没有说明 2-5MB 范围有什么特别之处,或者为什么它只适用于文档。
  • OP 想要的是监控文件上传进度。您的回答建议使用分块传输模式。不幸的是,此模式仅适用于流媒体内容。如果您的服务器不支持流式传输,您将收到错误消息。另一方面,您可以像@Phill 建议的那样子类化一个输出流,这将使您能够监控上传进度。我想不出你需要腾出 2-5MB 内存的用例,除非存在性能问题,而早期优化是万恶之源。顺便说一句,我从来没有说过它只适用于文档。
【解决方案2】:

我使用了 ChannelSocket 对象。该对象允许在访问服务器时进行阻塞读写。所以我使用这个套接字模拟了 HTTP 并且每个写请求都被阻塞,直到它完成。这样我就可以跟踪写事务的实际进度了。

【讨论】:

    【解决方案3】:

    另一种方法是轮询服务器,询问上传了多少数据。也许使用 id 或其他东西。

    POST -> /上传文件

    • 身份证
    • 文件

    POST -> /queryuploadstate(返回部分大小)

    • 身份证

    当然它需要连接 servlet 并且不允许集群...

    【讨论】:

      【解决方案4】:

      这将类似于您的下载方式。

      OutputStream stream = connection.getOutputStream();
      for(byte b: str.getBytes()) {                    
          totalBytes+=1;
          stream.write(b);
          if(totalBytes%50 == 0)
              stream.flush(); //bytes will be sent in chunks of 50
          //calculate something...
      }
      stream.flush();
      

      类似的东西。

      【讨论】:

      • 调用 write 不一定将这些字节发送到服务器,我需要弄乱响应时间。
      • 根据输出流,你正在写。不过……我可以稍微修改一下……
      • 它可以工作,但我仍然希望在发送每个数据包时收到通知,而无需明确配置发送的大小。
      • 啊……您可能想编辑您的问题来说明这一点。添加类似“我的问题是如何在每次发送数据包时收到通知?”
      • 这毕竟是行不通的,因为write方法是非阻塞方法,所以不管实际写什么时候它都会返回。
      【解决方案5】:

      如果您想计算您上传的字节数,请将对象作为字节数组写入输出流本身,就像您对输入流所做的那样。

      【讨论】:

      • 是的,但是如果我自己不把它弄丢的话,那会干扰文件被分块的方式。
      【解决方案6】:

      我在类似情况下所做的是创建FilterOutputStream 的子类,它会覆盖 write(...) 方法,然后可以通知您的代码任何写入的字节。

      【讨论】:

      • 嗨。我看到了这样的例子,但也许我不明白。我的意思是当我写入流时(使用一次写入方法)我确实知道我写了多少字节,但我看不到它的进度。它们可能仅在 flush 方法时被发送到服务器。
      • @Sophie 我也有类似的问题。字节被缓存并且仅在特定时间后写入。如果你知道的话,你可以通过提前设置内容长度来解决这个问题 - 请参阅 HttpURLConnection.setFixedLengthStreamingMode 了解更多信息。
      • 好的,谢谢,但如果我用大文本文件调用 write() 1 次,我不会看到正在发送的每个块的通知,对吧?
      • 如果您需要每个字节的通知,请执行 MirroredFate 给出的答案。一次写入一个字节(或一次写入少量字节)。
      • 不,不。理想的做法是在 API 向服务器发送的每个块上获得通知,而不会干扰通过网络将文件拆分为数据包的方式。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-26
      • 1970-01-01
      • 2011-04-02
      • 2013-10-31
      • 1970-01-01
      相关资源
      最近更新 更多