【问题标题】:Upload a file through an HTTP form, via MultipartEntityBuilder, with a progress bar通过 HTTP 表单上传文件,通过 MultipartEntityBuilder,带有进度条
【发布时间】:2013-09-28 15:31:17
【问题描述】:

短版 - 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,例如,将文件上传为“二进制/八位字节流”;不是“多部分/表单数据”。我需要真实的字段。

此示例File Upload with Java (with progress bar) 显示了如何覆盖*Entity*Stream。所以也许我可以告诉MultipartEntityBuilder.create() 一个覆盖其上传进度的实体?

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

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

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

【问题讨论】:

  • 谢谢例如。正是我需要的!
  • 有人能告诉我在哪里可以获得适合 MultipartEntityBuilder 的库吗?
  • 根据“org.apache.http.entity.mime”,它可能是 httpmime-4.3.jar。我在另一条评论中列出了我的罐子......
  • Philip 我想我有一个类似的问题,八进制流不是真正的字段.. Api 拒绝它.. 我很好奇你是否可以看看我的最新帖子并给出你的想法跨度>

标签: android http file-upload


【解决方案1】:

获奖代码(采用壮观的 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??

【讨论】:

  • builder.addTextBody("macAddress", macAddress); 在 http POST 中添加了什么?表单域?当我使用 multipart/form builder 发布请求时,我在服务器上的请求中看不到任何此类数据。
  • 作为一个 Java 新手,我所能做的就是报告我的 JAR:android-support-v4.jar google-api-client-1.17.0-rc.jar google-api-client -android-1.17.0-rc.jar google-http-client-1.17.0-rc.jar google-play-services.jar httpcore-4.3.jar httpmime-4.3.jar。您可以考虑一个新问题,因此它会出现在排行榜中...
  • 如果你有一个 Uri 而不是文件名,请检查这个答案:stackoverflow.com/questions/3401579/…
  • 如果有人需要,可以从这里下载必要的文件:java2s.com/Code/Jar/h/Downloadhttpmime43jar.htm
  • 如果您对 FileBody 有疑问,那么您包含了较旧的 jar。我刚刚从repo1.maven.org/maven2/org/apache/httpcomponents 下载了 4.3 版本的 httpclient 和 httpcore,它就像一个魅力。
【解决方案2】:

非常感谢 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);
            }

【讨论】:

  • 回信!正是因为这个原因,我忽略了实际的进度条:对我来说,它是UploadTask.this.publishProgress(totalInBlocks - blocks)。我不想将特定于 Android 的代码放入通用答案中。
  • 另一项改进 - 仅在当前结束 > 1 000 或足够的结束值已累积到局部变量时输出 .publish。
  • 和孩子们,请注意 MK 将实际的“totalSent”降低了 100。我的步数也是如此,但是通过我们正在执行的这个特定的有线协议强制执行的块大小。然而,在这两种情况下,我们都利用并且不滥用关于显示更新的“publish()”概念。进度条必须知道其当前位置,甚至无需调用 GPU 进行重新计算,因此将 if 放在该函数调用的另一侧是避免发布参数更混乱的最有效方法。要点是:以算法或任意方式步进进度条。
  • 如何获取totalSize?我正在向服务器发送图像。是图像的总大小还是其他?
  • 图像的总大小 - 不是在线上的乐趣 - 因为进度条只测量文件。在认真使用时,应该有人在两端添加软糖因子,这样酒吧就会变得更平滑
【解决方案3】:

首先:非常感谢原始问题/答案。由于 HttpPost 现在已被弃用,我对其进行了一些修改,使用来自 article 的额外输入并制作了一个微型库:https://github.com/licryle/HTTPPoster

它将整体包装在异步任务中;使用 MultipartEntityBuilder 和 HttpURLConnection 并让您监听回调。

使用方法:

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

    • onStartTransfer
    • 正在进行中
    • onFailure
    • 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);

希望能有所帮助:) 也欢迎提出改进建议!它是最新的,我会根据需要对其进行扩展。

干杯

【讨论】:

  • 终于有人清理了我的“Java异端”
猜你喜欢
  • 2011-02-12
  • 1970-01-01
  • 2019-10-17
  • 1970-01-01
  • 1970-01-01
  • 2013-06-05
  • 2021-11-14
  • 1970-01-01
  • 2014-12-24
相关资源
最近更新 更多