HttpUrlConnection 多部分文件上传与progressBar [英] HttpUrlConnection multipart file upload with progressBar

查看:37
本文介绍了HttpUrlConnection 多部分文件上传与progressBar的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想通过HttpUrlConnection查看上传文件的进度.我怎么能做到这一点?我尝试在 OutputStream 中写入数据时计算字节,但这是错误的,因为只有在调用 conn.getInputStream() 时才会发生真正的上传,所以我需要以某种方式检查输入流.这是我的代码:

I want to check progress of uploading file by HttpUrlConnection. How I can do this? I've tried to calculate bytes when writing data in OutputStream but it's wrong, cause real uploading happens only when I call conn.getInputStream(), so I need somehow to check inputStream. Here is my code:

public static void uploadMovie(final HashMap<String, String> dataSource, final OnLoadFinishedListener finishedListener, final ProgressListener progressListener) {
  if (finishedListener != null) {
    new Thread(new Runnable() {
       public void run() {
         try {

              String boundary = getMD5(dataSource.size()+String.valueOf(System.currentTimeMillis()));
              MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create();
              multipartEntity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);    
              multipartEntity.setCharset(Charset.forName("UTF-8"));

              for (String key : dataSource.keySet()) {
                 if (key.equals(MoviesFragmentAdd.USERFILE)) {
                    FileBody  userFile = new FileBody(new File(dataSource.get(key)));
                    multipartEntity.addPart(key, userFile);
                    continue;
                 }
                 multipartEntity.addPart(key, new StringBody(dataSource.get(key),ContentType.APPLICATION_JSON));
              }

              HttpEntity entity = multipartEntity.build();
              HttpURLConnection conn = (HttpsURLConnection) new URL(URL_API + "/video/addForm/").openConnection();
              conn.setUseCaches(false);
              conn.setDoOutput(true);
              conn.setDoInput(true);
              conn.setRequestMethod("POST");
              conn.setRequestProperty("Accept-Charset", "UTF-8");
              conn.setRequestProperty("Connection", "Keep-Alive");
              conn.setRequestProperty("Cache-Control", "no-cache");
              conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
              conn.setRequestProperty("Content-length", entity.getContentLength() + "");
              conn.setRequestProperty(entity.getContentType().getName(),entity.getContentType().getValue());

              OutputStream os = conn.getOutputStream();
              entity.writeTo(os);
              os.close();

              //Real upload starting here -->>

              BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));

              //<<--

              JsonObject request = (JsonObject) gparser.parse(in.readLine());
              if (!request.get("error").getAsBoolean()) {
              //do something
              }
              conn.disconnect(); 

           } catch (Exception e) {
            e.printStackTrace();
           }
         }
    }).start();

  }
}

推荐答案

因为你要处理上传,我想大部分时间都在做 entity.writeTo(os);.也许与服务器的第一次联系也需要一些时间(DNS 解析,SSL 握手,...).您为实际上传"设置的标记不正确 IMO.

Because you have to deal with upload, I'd suppose most time is taken when doing entity.writeTo(os);. Maybe the first contact to the server takes some time as well (DNS resolution, SSL-handshake, ...). The markers you set for "the real upload" are not correct IMO.

现在取决于你的Multipart-library,你是否可以拦截writeTo.如果它既聪明又节省资源,它就会遍历各个部分并将内容逐一流式传输到输出流.如果没有,并且 .build() 操作正在创建一个大的 byte[],那么您可以获取这个数组,将其分块流式传输到服务器并告诉您的用户已经完成了多少百分比的上传.

Now it depends on your Multipart-library, whether you can intercept writeTo. If it is clever and resource-efficient, it's iterating over the parts and streams the content one-by-one to the output stream. If not, and the .build() operation is creating a big fat byte[], then you could take this array, stream it in chunks to the server and tell your user how many percent of the upload is already done.

从资源的角度来看,我宁愿不知道会发生什么.但是,如果反馈非常重要,并且电影只有几兆字节,您可以先将 Multipart-Entity 流式传输到 ByteArrayOutputStream,然后将创建的字节数组的小块写入服务器同时通知您的用户有关进度.以下代码未经验证或测试(您可以将其视为伪代码):

From a resource perspective I'd prefer not really knowing what happens. But if feedback is that important and if the movies are only a few megabytes in size, you could stream the Multipart-Entity first to a ByteArrayOutputStream and then write little chunks of the created byte-array to the server while notifying your user about progress. The following code is not validated or tested (you can see it as pseudo-code):

ByteArrayOutputStream baos = new ByteArrayOutputStream();
entity.writeTo(baos);
baos.close();
byte[] payload = baos.toByteArray();
baos = null;

OutputStream os = conn.getOutputStream();

int totalSize = payload.length;
int bytesTransferred = 0;
int chunkSize = 2000;

while (bytesTransferred < totalSize) {
    int nextChunkSize = totalSize - bytesTransferred;
    if (nextChunkSize > chunkSize) {
        nextChunkSize = chunkSize;
    }
    os.write(payload, bytesTransferred, nextChunkSize); // TODO check outcome!
    bytesTransferred += nextChunkSize;

    // Here you can call the method which updates progress
    // be sure to wrap it so UI-updates are done on the main thread!
    updateProgressInfo(100 * bytesTransferred / totalSize);
}
os.close();

更优雅的方法是编写一个拦截 OutputStream,它记录进度并将真正的写入操作委托给底层的真实"OutputStream.

A more elegant way would be to write an intercepting OutputStream which registers progress and delegates the real write-operations to the underlaying "real" OutputStream.

编辑

@whizzzkey 写道:

@whizzzkey wrote:

我已经重新检查了很多次 - entity.writeTo(os) 不会进行真正的上传,它会进行 conn.getResponseCode() 或 <代码>conn.getInputStream()

I've re-checked it many times - entity.writeTo(os) DOESN'T do a real upload, it does conn.getResponseCode() or conn.getInputStream()

现在很清楚了.HttpURLConnection 正在缓冲您的上传数据,因为它不知道内容长度.您已设置标题内容长度",但显然 HUC 会忽略此标题.你必须打电话

Now it's clear. HttpURLConnection is buffering your upload data, because it doesn't know the content-length. You've set the header 'Content-length', but oviously this is ignored by HUC. You have to call

conn.setFixedLengthStreamingMode(entity.getContentLength());

那你最好去掉对 conn.setRequestProperty("Content-length", entity.getContentLength() + "");

在这种情况下,HUC 可以写入标头,entity.writeTo(os) 可以真正将数据流式传输到服务器.否则,当 HUC 知道将传输多少字节时,将发送缓冲数据.所以实际上 getInputStream() 告诉 HUC 你已经完成了,但是在真正读取响应之前,所有收集到的数据都必须发送到服务器.

In this case, HUC can write the headers and entity.writeTo(os) can really stream the data to the server. Otherwise the buffered data is sent when HUC knows how many bytes will be transferred. So in fact, getInputStream() tells HUC that you're finished, but before really reading the response, all the collected data has to be sent to the server.

我不建议更改您的代码,但是对于那些不知道传输数据的确切大小(以字节为单位,而不是字符!!)的人来说,您可以告诉 HUC 它应该将数据传输到没有设置确切内容长度的块:

I wouldn't recommend changing your code, but for those of you who don't know the exact size of the transferred data (in bytes, not characters!!), you can tell HUC that it should transfer the data in chunks without setting the exact content-length:

conn.setChunkedStreamingMode(-1); // use default chunk size

这篇关于HttpUrlConnection 多部分文件上传与progressBar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆