【问题标题】:Get Progress of File Upload using HttpPost in Android在 Android 中使用 HttpPost 获取文件上传进度
【发布时间】:2014-10-17 03:54:01
【问题描述】:

我正在尝试使用 HttpPost 获取实际文件上传的进度。到目前为止,我有一个稳定的解决方案(我在 SO 中找到),但在上传大文件后我意识到它只计算写入输出缓冲区的字节而不是传输后的进度。我想以某种方式获得实际“帖子”的进展。有人可以解释我如何使用我到目前为止努力工作的成果来实现这一目标吗?我在网上找到的大多数解决方案都只计算写入输出缓冲区的字节数(这对于小文件来说已经足够了,但在传输大文件时就不行了)。

public static String postFile(final Context context, String fileName) throws Exception {

    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost("http://my.url/");
    MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);

    final File file = new File(fileName);
    final long totalSize = file.length();
    FileBody fb = new FileBody(file);

    builder.addPart("uploaded_file", new FileBody(new File(fileName)));


    final HttpEntity yourEntity = builder.build();

    int progressPercent = 0;

    class ProgressiveEntity implements HttpEntity {
        @Override
        public void consumeContent() throws IOException {
            yourEntity.consumeContent();                
        }
        @Override
        public InputStream getContent() throws IOException,
                IllegalStateException {
            return yourEntity.getContent();
        }
        @Override
        public Header getContentEncoding() {             
            return yourEntity.getContentEncoding();
        }
        @Override
        public long getContentLength() {
            return yourEntity.getContentLength();
        }
        @Override
        public Header getContentType() {
            return yourEntity.getContentType();
        }
        @Override
        public boolean isChunked() {             
            return yourEntity.isChunked();
        }
        @Override
        public boolean isRepeatable() {
            return yourEntity.isRepeatable();
        }
        @Override
        public boolean isStreaming() {             
            return yourEntity.isStreaming();
        } // CONSIDER put a _real_ delegator into here!

        @Override
        public void writeTo(OutputStream outstream) throws IOException {

            class ProxyOutputStream extends FilterOutputStream {

                public ProxyOutputStream(OutputStream proxy) {
                    super(proxy);    
                }
                public void write(int idx) throws IOException {
                    out.write(idx);
                }
                public void write(byte[] bts) throws IOException {
                    out.write(bts);
                }
                public void write(byte[] bts, int st, int end) throws IOException {
                    out.write(bts, st, end);
                }
                public void flush() throws IOException {
                    out.flush();
                }
                public void close() throws IOException {
                    out.close();
                }
            } // CONSIDER import this class (and risk more Jar File Hell)

            class ProgressiveOutputStream extends ProxyOutputStream {
                long totalSent;
                public ProgressiveOutputStream(OutputStream proxy) {
                       super(proxy);
                       totalSent = 0;
                }

                public void write(byte[] bts, int st, int end) throws IOException {

                // end is the amount being sent this time
                // st is always zero and end=bts.length()

                     totalSent += end;
                     int progress = (int) ((totalSent / (float) totalSize) * 100);
                     out.write(bts, st, end);
                }
            }

            yourEntity.writeTo(new ProgressiveOutputStream(outstream));
        }

    };



    ProgressiveEntity myEntity = new ProgressiveEntity();

    post.setEntity(myEntity);

    //Output to buffer is complete at this point!
    HttpResponse response = client.execute(post);        

    String jsonResponseStr = getContent(response);

    Log.d("MYTAG",jsonResponseStr);

    return jsonResponseStr;

} 

在远程服务器上的接收脚本中,我只是回显一个字符串,以便我可以立即发送响应(根本没有文件/数据库处理),而来自服务器的响应仍然需要很长时间。我坚信此时传输发生在写入缓冲区完成后。

【问题讨论】:

  • 使用 publishProgress();检查this
  • @ashutiwari4 除非您使用我不认为我使用的任何方法的 setfixedlengthstreamingmode 选项,否则该答案还计算写入缓冲区进度。如果可能的话,我想避免重写我所拥有的一切。
  • NOT the post transfer progress.。什么是转后进度?
  • @greenapps 当您写入缓冲区时,POST 并未真正开始。写入缓冲区完成后,开始发送到远程 url。我想跟踪 POST 的进度
  • 所以你说我可以将一个 5 MB 的文件写入输出流,然后实际传输将开始?好吧,我很难相信。

标签: java android http-post multipartentity


【解决方案1】:
class ProgressiveOutputStream extends ProxyOutputStream {
            long totalSent;
            public ProgressiveOutputStream(OutputStream proxy) {
                   super(proxy);
                   totalSent = 0;
            }

            public void write(byte[] bts, int st, int end) throws IOException {

            // FIXME  Put your progress bar stuff here!
            // end is the amount being sent this time
            // st is always zero and end=bts.length()

                 totalSent += end;
                 progress.publish((int) ((totalSent / (float) totalSize) * 100));
                 out.write(bts, st, end);
            }

【讨论】:

    【解决方案2】:

    由于我没有看到任何解决方案,我想答案是使用没有进度百分比的旋转动画。因为无论如何在传输完成之前什么都做不了。哦,好吧……至少它解决了我的问题。

    【讨论】:

    • 我仍然认为你在谈论一个不存在的问题。
    • 如果我能想象到这一点,您能否解释一下为什么在我的进度达到 100% 后,上传的文件才刚刚开始在我的远程服务器上显示为总文件大小的一小部分?为什么在服务器上的文件达到其总大小之前服务器响应不会返回到 android 客户端?以及为什么如果我删除所有服务器端文件处理并立即返回响应,服务器响应到达客户端仍然需要相同的时间。也许我对它的缓冲有误,但这对我来说似乎是个问题。
    • why after my progress reaches 100%, the uploaded file has just begun to show up as only a fraction of the total file size on my remote server? 这些是缓存和缓冲问题。一个准备填充缓冲区,另一个忙于清空它们。 why the server response doesnt come back to android client until file on server has reached its total size? 因为服务器只有在一切都“in”时才会开始响应。服务器在读取所有内容之前甚至无法发送任何内容,因为这是我之前说过的 http 协议所禁止的。
    • 是的,没错!感谢您的确认
    【解决方案3】:

    我知道这已经很老了,但我刚刚找到了解决方案。 如果 totalSize 是您的实体内容长度,那么您可以以此为基础:

    class ProgressiveOutputStream extends ProxyOutputStream {
                        long totalSent;
                        long totalSize;
                        public ProgressiveOutputStream(OutputStream proxy, long total) {
                            super(proxy);
                            totalSent = 0;
                            totalSize = total;
                        }
                        public void write(byte[] bts, int st, int end) throws IOException {
                            totalSent += end;
                            publishProgress((int) ((totalSent / (float) totalSize) * 100));
                            out.write(bts, st, end);
                        }
                    }
    
                    yourEntity.writeTo(new ProgressiveOutputStream(outstream, yourEntity.getContentLength()));
    

    您在 asyncTask 的 OnProgressUpdate 中更新您的进度条(pb 是进度条):

    @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            pb.setProgress(values[0]);
        }
    

    【讨论】:

    • 谢谢,但不幸的是我使用了 Thread 而不是 AsyncTask,我现在没有时间尝试这个。但如果我需要修改这件作品,我会试一试。
    【解决方案4】:

    也许你可以在每次调用 write 时刷新数据:

    totalSent += end;
    int progress = (int) ((totalSent / (float) totalSize) * 100);
    out.write(bts, st, end);
    out.flush(); //flush
    

    编辑: 你也可以试试这个,因为我觉得 end 代表输出流中名为 out 的结束索引:

    totalSent += (end-st);
    

    【讨论】:

    • 谢谢,我试过了,但没有发现与 92mb 视频有任何区别。实际上,无论是否有 flush 语句,我都没有注意到任何滞后,所以不能说这是否有任何区别。
    • 我感觉end 的值实际上并不是发送的数量,而是最终索引。我知道它说它是文档中发送的数量,但我认为它表示为结束索引,因此当您调用 out.wrtite(bts, st, end); 时,它会正确处理它。我知道它看起来不对,但您可以试试这个:totalSent += (end - st);而是
    【解决方案5】:

    请尝试下一个解决方案,而不是在您的 writeTo 方法中使用 ProxyOutputStream

    @Override 
    public void writeTo(OutputStream outstream) throws IOException {
        ByteArrayInputStream reader = new ByteArrayInputStream(mImageData);
        byte[] fileBuffer = new byte[2048];
        int bytesRead;
        while ((bytesRead = reader.read(fileBuffer)) != -1) {
            outstream.write(fileBuffer, 0, bytesRead);
            int progress = bytesRead;
        }
        reader.close();
        yourEntity.writeTo(outstream);
    }
    

    【讨论】:

    • 为什么要尝试这个解决方案而不是那个解决方案?
    • OP 提到他的解决方案无法正常工作。我认为他的解决方案没有正确计算发送的字节数。如果您尝试一下,您会注意到最终进程中只有一半的总字节数。我的解决方案返回了我从 reader.read(fileBuffer) 方法获得的正确发送字节数。
    猜你喜欢
    • 2014-04-04
    • 2014-04-07
    • 2010-11-02
    • 2014-07-05
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 2013-08-02
    • 2016-12-07
    相关资源
    最近更新 更多