使用progressBar上传HttpUrlConnection多部分文件

时间:2014-03-20 03:58:14

标签: android upload httpurlconnection android-progressbar multipart

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

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

  }
}

2 个答案:

答案 0 :(得分:13)

因为您必须处理上传,我认为在执行entity.writeTo(os);时会花费大部分时间。也许与服务器的第一次联系也需要一些时间(DNS解析,SSL握手......)。您设置的标记&#34;真正的上传&#34;是不正确的IMO。

现在它取决于您的Multipart库,是否可以拦截writeTo。如果它是聪明且资源有效的,它会迭代部件并将内容逐个流式传输到输出流。如果没有,并且.build()操作正在创建一个大胖byte[],那么您可以获取此数组,将其以块的形式流式传输到服务器并告诉您的用户已经完成了多少百分比的上传。

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

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,它注册进度并将实际的写操作委托给底层的&#34; real&#34;输出流。

修改

@whizzzkey写道:

  

我已经多次重新检查过了 - entity.writeTo(os)做了真正的上传,conn.getResponseCode()conn.getInputStream()

现在很清楚。 HttpURLConnection正在缓存您的上传数据,因为它不知道内容长度。您已设置标题&#39;内容长度&#39;但显然HUC会忽略此标题。你必须致电

conn.setFixedLengthStreamingMode(entity.getContentLength());

然后您最好删除对conn.setRequestProperty("Content-length", entity.getContentLength() + "");

的调用

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

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

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

答案 1 :(得分:-2)

在您的活动中使用此代码......

公共类PublishPostToServer扩展了AsyncTask实现 ProgressListenerForPost {

    public Context pContext;
    public long totalSize;
    private String response;

    public PublishPostToServer(Context context) {
        pContext = context;

    }

    protected void onPreExecute() {
        showProgressDialog();
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        boolean success = true;
        try {
            response = NetworkAdaptor.getInstance()
                    .upLoadMultipartImageToServer(
                            "",
                            "",
                            "", this, this); // Add file path, Authkey, caption 

        } catch (Exception e) {
            success = false;
        }
        return success;
    }

    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);
        //validateResponse(result, response);
    }

    @Override
    protected void onProgressUpdate(Integer... values) {

        try {
            if (mProgressDialog != null) {
                mProgressDialog.setProgress(values[0]);
            }
        } catch (Exception exception) {

        }
    }

    @Override
    public void transferred(long num) {
        publishProgress((int) ((num / (float) totalSize) * 100));
    }

}

private void showProgressDialog() {

    try {
        String dialogMsg = "Uploading Image...";
        mProgressDialog = new ProgressDialog(this);
        mProgressDialog.setMessage(dialogMsg);
        mProgressDialog.setIndeterminate(false);
        mProgressDialog.setMax(100);
        mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        mProgressDialog.setCancelable(false);
        mProgressDialog.show();
    } catch (Exception exception) {

    }
}

现在,制作一个NetworkAdapter类

public String upLoadMultipartImageToServer(String sourceFileUri,             String auth_key,String caption,ProgressListenerForPost listiner,             PublishPostToServer asyncListiner){         String upLoadServerUri =&#34;&#34; +&#34; upload_image&#34;;

    HttpPost httppost = new HttpPost(upLoadServerUri);

    File file = new File(sourceFileUri);

    if (file.exists()) {

        FileBody filebodyVideo = new FileBody(file);
        CustomMultiPartEntity multipartEntity = new CustomMultiPartEntity(
                HttpMultipartMode.BROWSER_COMPATIBLE, listiner);
        try {
            multipartEntity.addPart("auth_key", new StringBody(auth_key));
            multipartEntity.addPart("caption", new StringBody(caption));
            multipartEntity.addPart("image", filebodyVideo);
            asyncListiner.totalSize = multipartEntity.getContentLength();
            httppost.setEntity(multipartEntity);

        }

        catch (UnsupportedEncodingException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        DefaultHttpClient mHttpClient = new DefaultHttpClient();
        String response = "";
        try {
            response = mHttpClient.execute(httppost,
                    new MovieUploadResponseHandler());
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return response;
    } else {
        return null;
    }

} 

@SuppressWarnings("rawtypes")
private class MovieUploadResponseHandler implements ResponseHandler {

    @Override
    public Object handleResponse(HttpResponse response)
            throws ClientProtocolException, IOException {

        HttpEntity r_entity = response.getEntity();
        String responseString = EntityUtils.toString(r_entity);
        // DebugHelper.printData("UPLOAD", responseString);

        return responseString;
    }

}

public static boolean isValidResponse(String resultData) {
    try {

    } catch (Exception exception) {
        //DebugHelper.printException(exception);
    }
    return true;
}

public String upLoadVideoToServer(String currentFilePath, String string,
        PublishPostToServer publishPostToServer,
        PublishPostToServer publishPostToServer2) {
    // TODO Auto-generated method stub
    return null;
}