Java 在Android中使用HttpPost获取文件上传进度

Java 在Android中使用HttpPost获取文件上传进度,java,android,http-post,multipartentity,Java,Android,Http Post,Multipartentity,我正在尝试使用HttpPost获取实际文件上传的进度。到目前为止,我有一个稳定的解决方案(我在这里找到了),它有一个进展,但在上传大文件后,我意识到它只计算写入输出缓冲区的字节数,而不是传输后的进度。我想以某种方式了解实际“发布”的进度。有人能解释一下,我是如何用我迄今为止所付出的努力来实现这一目标的吗?我在网上找到的大多数解决方案都只是计算写入输出缓冲区的字节数(这对于小文件来说已经足够了,但在传输大文件时就不行了) 公共静态字符串postFile(最终上下文,字符串文件名)引发异常{

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

公共静态字符串postFile(最终上下文,字符串文件名)引发异常{

    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;

} 

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

因为我没有看到任何解决方案,我想答案是使用旋转动画,没有进度百分比。因为在传输完成之前无法完成任何操作。哦,我们嗯……至少它解决了我的问题。

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

totalSent += end;
int progress = (int) ((totalSent / (float) totalSize) * 100);
out.write(bts, st, end);
out.flush(); //flush
编辑: 您也可以尝试这样做,因为我感觉end表示名为
out
的outputstream中的end索引:

totalSent += (end-st);

我知道这很古老,但我刚刚找到了解决办法。 如果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是进度条):


请尝试下一个解决方案,而不是在
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);
}

使用publishProgress();检查@ashutiwari4,除非您使用setfixedlengthstreamingmode选项,否则答案也会计算写入缓冲区的进度。我认为这不是我使用的任何方法。如果可能,我希望避免重写我拥有的所有内容。
不是传输后进度。
。什么是传输后进度?@greenapps在写入缓冲区后,POST实际上还没有开始。写入缓冲区完成后,POST到远程url开始。我想跟踪POST的进度,所以你说我可以将一个5 MB的文件写入输出流,当完成后,实际传输将开始?我很难相信这一点。我仍然认为你在谈论一个问题这是不存在的。如果我是这么想的,你能解释一下为什么在我的进度达到100%后,上传的文件在我的远程服务器上只显示为总文件大小的一小部分?为什么服务器响应在服务器上的文件达到总大小之前不会返回到android客户端?为什么我要删除所有服务器在服务器端处理文件并返回即时响应后,服务器响应到达客户端所需的时间仍然相同。也许我对缓冲的理解是错误的,但这就是我的问题所在。
为什么在我的进度达到100%后,上传的文件只开始显示为总文件的一小部分我的远程服务器上的大小?
这些都是缓存和缓冲问题。一个是准备填充缓冲区,另一个是忙着清空缓冲区。
为什么服务器响应在服务器上的文件达到其总大小之前不会返回到安卓客户端?
因为服务器只有在所有文件都“进入”时才会开始响应。服务器甚至无法启动在它读取所有内容之前结束某些内容,因为我之前说过http协议禁止这样做。是的,没错!感谢您确认Hanks,但不幸的是我使用了Thread而不是AsyncTask,我现在没有时间尝试。但是如果我需要修改这篇文章,我会尝试一下。谢谢,我尝试了这个,但没有注意到e与92mb视频有任何区别。实际上,我没有注意到任何带有或不带有flush语句的延迟,因此不能说这是否有任何区别。我感觉值
end
实际上不是发送的量,而是结束索引。我知道它说的是文档中发送的量,但我认为它表示为结束索引的值o当你调用
out.write(bts,st,end);
它能正确处理它。我知道它看起来不正确,但你能试试这个:
totalSent+=(end-st)
为什么要尝试这个解决方案而不是那个解决方案?OP提到他的解决方案工作不正常。我发现他的解决方案没有正确计算发送的字节数。如果您愿意尝试,您会注意到最终处理的字节数只占总字节数的一半。我的解决方案返回正确的发送字节数
reader.read(文件缓冲区)
方法。
@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);
}
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);
            }