通过HTTP表单,通过MultipartEntityBuilder上传文件,并带有进度条



简短版本 - org.apache...MultipartEntity已被弃用,其升级MultipartEntityBuilder在我们的在线论坛中似乎代表性不足。让我们解决这个问题。如何注册回调,以便我的 (Android) 应用程序可以在上传文件时显示进度条?

长版本 - 这是MultipartEntityBuilder的"缺失的污垢简单示例":

public static void postFile(String fileName) throws Exception {
    // Based on: https://stackoverflow.com/questions/2017414/post-multipart-request-with-android-sdk
    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost(SERVER + "uploadFile");
    MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    builder.addPart("file", new FileBody(new File(fileName)));
    builder.addTextBody("userName", userName);
    builder.addTextBody("password", password);
    builder.addTextBody("macAddress", macAddress);
    post.setEntity(builder.build());
    HttpResponse response = client.execute(post);
    HttpEntity entity = response.getEntity();
    // response.getStatusLine();  // CONSIDER  Detect server complaints
    entity.consumeContent();
    client.getConnectionManager().shutdown(); 
}  // FIXME  Hook up a progress bar!

我们需要修复该FIXME 。(另一个好处是可中断的上传。但是(无论我是否错了,请纠正我),所有在线示例似乎都不足。

例如,这个文件 http://pastebin.com/M0uNZ6SB 上传为"二进制/八位字节流";而不是"多部分/表单数据"。我需要真实的领域。

此示例使用 Java 上传文件(带进度条)显示了如何覆盖*Entity*Stream。因此,也许我可以告诉MultipartEntityBuilder .create()一个覆盖的实体来计量其上传进度?

因此,如果我想覆盖某些内容,并将内置流替换为每 1000 个字节发送一个信号的计数流,也许我可以扩展FileBody部分,并覆盖其getInputStream和/或writeTo

但是当我尝试class ProgressiveFileBody extends FileBody {...}时,我得到了臭名昭著的java.lang.NoClassDefFoundError

因此,当我在.jar文件中搜寻丢失的 Def 时,有人可以检查我的数学,并指出我忽略的更简单的修复吗?

获胜的代码(以壮观的Java-Heresy(tm)风格)是:

public static String postFile(String fileName, String userName, String password, String macAddress) throws Exception {
    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost(SERVER + "uploadFile");
    MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
    builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
    final File file = new File(fileName);
    FileBody fb = new FileBody(file);
    builder.addPart("file", fb);  
    builder.addTextBody("userName", userName);
    builder.addTextBody("password", password);
    builder.addTextBody("macAddress",  macAddress);
    final HttpEntity yourEntity = builder.build();
    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 {
                /**
                 * @author Stephen Colebourne
                 */
                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 {
                public ProgressiveOutputStream(OutputStream proxy) {
                    super(proxy);
                }
                public void write(byte[] bts, int st, int end) throws IOException {
                    // FIXME  Put your progress bar stuff here!
                    out.write(bts, st, end);
                }
            }
            yourEntity.writeTo(new ProgressiveOutputStream(outstream));
        }
    };
    ProgressiveEntity myEntity = new ProgressiveEntity();
    post.setEntity(myEntity);
    HttpResponse response = client.execute(post);        
    return getContent(response);
} 
public static String getContent(HttpResponse response) throws IOException {
    BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
    String body = "";
    String content = "";
    while ((body = rd.readLine()) != null) 
    {
        content += body + "n";
    }
    return content.trim();
}
#  NOTE ADDED LATER: as this blasterpiece gets copied into various code lineages, 
#  The management reminds the peanut gallery that "Java-Heresy" crack was there
#  for a reason, and (as commented) most of that stuff can be farmed out to off-
#  the-shelf jar files and what-not. That's for the java lifers to tool up. This
#  pristine hack shall remain obviousized for education, and for use in a pinch.
#  What are the odds??

不能感谢Phlip的解决方案。以下是添加进度条支持的最后润色。我在 AsyncTask 中运行了它 - 下面的进度使您能够将进度发布回 AsyncTask 中的方法,该方法为在 AsyncTask 中运行的类调用 AsyncTask.publishProgress()。进度条并不完全平滑,但至少它会移动。在三星S4上,在序言之后上传4MB的图像文件,它正在移动4K块。

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

首先:非常感谢原始问题/答案。由于 HttpPost 现已弃用,我使用本文中的其他输入对其进行了重新设计,并制作了一个微型库:https://github.com/licryle/HTTPPoster

它将整体包装在ASync任务中;使用MultipartEntityBuilder和HttpURLConnection,让你监听回调。

要使用:

  1. 下载和提取
  2. 在 build.gradle 模块文件中,添加依赖项:
dependencies 
{    
     compile project(':libs:HTTPPoster') 
}
  1. 您需要一个类来实现 HttpListener 接口,以便可以侦听回调。它在HTTPListener有四个回调:

    • 开始传输
    • 进行中
    • 失败
    • onResponse
  2. 配置 ASyncTask 并启动它。下面是一个快速用法:

HashMap<String, String> mArgs = new HashMap<>();
mArgs.put("lat", "40.712784");
mArgs.put("lon", "-74.005941");
ArrayList<File> aFileList = getMyImageFiles();
HttpConfiguration mConf = new HttpConfiguration(
    "http://example.org/HttpPostEndPoint",
    mArgs,
    aFileList,
    this, // If this class implements HttpListener
    null,  // Boundary for Entities - Optional
    15000  // Timeout in ms for the connection operation
    10000, // Timeout in ms for the reading operation
);
new HttpPoster().execute(mConf);

希望可以帮助:)您也可以随时提出改进建议!这是最近的,我会根据需要扩展它。

干杯

最新更新