多部分文件进度更新比实际上传更快

Multipart file progress updating faster than actual upload

我正在尝试使用 Multipart 将文件发送到服务器。 这就是我发送请求的方式

MultipartRequest request = new MultipartRequest(putURLPath,
                    hashMap,
                    null,
                    new Response.Listener<String>() {
                        @Override
                        public void onResponse(String response) {
                            try {
                                Log.d(TAG, "FILE_UPLOAD_RESPONSE: " + response);
                                Logger.writeToFile(TAG, "FILE_UPLOAD_RESPONSE: " + response);

                                if (fileUploadListener != null)
                                    fileUploadListener.onAttachmentFileUpload(generateImageUploadResponse(false, getURLPath), message, isRetry);
//                                fileDownload(getURLPath);
                            } catch (Exception e) {
                                e.printStackTrace();
                                if (fileUploadListener != null)
                                    fileUploadListener.onAttachmentFileUpload(generateImageUploadResponse(true, ""), message, isRetry);
                            }
                        }
                    }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    Logger.writeToFile(TAG, "FILE_UPLOAD_RESPONSE--Error: " + error.getMessage());
                   /* byte[] bytes = ((NetworkResponse)((AuthFailureError)error).networkResponse).data;
                    WAAFILogger.d(TAG, "XML: " + bytes.);*/
                    if (fileUploadListener != null)
                        fileUploadListener.onAttachmentFileUpload(generateImageUploadResponse(true, ""), message, isRetry);
                    error.printStackTrace();
                }
            }, new IMultipartProgressListener() {
                @Override
                public void transferred(long transferred, int progress) {
                    WAAFILogger.d(TAG, "Transferred : " + transferred + "\n" + " Progress : " + progress);

                    try {
                        if (fileUploadListener != null) {
                            fileUploadListener.onProgressChanged(message, progress);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }, bytesArray) {

                @Override
                public String getBodyContentType() {
                    return fileInfo.fileMimeType;
                }

                @Override
                public Map<String, String> getHeaders() throws AuthFailureError {
                    Map<String, String> params = new HashMap<String, String>();
                    params.put("content-length", String.valueOf(bytesArray.length));
                    params.put("host", IMConstant.XMPP_SERVER + ":9000");

                    Logger.writeToFile(TAG, params.get("content-length"));
                    Logger.writeToFile(TAG, params.get("host"));
                    return params;
                }

            };


            request.setRetryPolicy(com.safarifone.settings.Settings.policy);
            VolleyQueManager.getInstance().addToRequestQueue(request);

我使用了自定义请求,link 在这里 Custom Request Repo Link 代码在下面

public class MultipartRequest extends Request<String> {

    private final byte[] byteArray;
    MultipartEntityBuilder entity = MultipartEntityBuilder.create();
    //        CounterHttpEntity httpentity;
    HttpEntity httpentity;
    //        CountingHttpEntity httpentity;
    private FileUploadManager.IMultipartProgressListener mProgressListener;

    private final Response.Listener<String> mListener;
    private HashMap<String, File> mFiles;
    private HashMap<String, String> mBody;
    private long fileLength = 0L;

    public MultipartRequest(String url,
                            HashMap<String, File> mFiles,
                            HashMap<String, String> body,
                            Response.Listener<String> listener,
                            Response.ErrorListener errorListener,
                            FileUploadManager.IMultipartProgressListener progressListener, byte[] bytesArray) {

        super(Method.PUT, url, errorListener);

        this.mListener = listener;
        this.mFiles = mFiles;
        this.mBody = body;
        this.fileLength = /*getFileLength()*/ bytesArray.length;
        this.mProgressListener = progressListener;
        this.byteArray = bytesArray;

        entity.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        try {
            entity.setCharset(CharsetUtils.get("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        buildMultipartEntity();

        httpentity = entity.build();
        /*httpentity.setFileLength(fileLength);
        httpentity.setmProgressListener(progressListener);*/

    }

    private void buildMultipartEntity() {    
        for (Map.Entry<String, File> entry : mFiles.entrySet()) {
            if (entry.getValue() != null) {
                entity.addPart(entry.getKey(), new FileBody(entry.getValue()));
            }
        }

        if (mBody != null) {
            for (Map.Entry<String, String> entry : mBody.entrySet()) {
                if (entry.getValue() != null) {
                    entity.addTextBody(entry.getKey(), entry.getValue());
                }
            }
        }
    }

    private int getFileLength() {
        int lgth = 0;
        for (Map.Entry<String, File> entry : mFiles.entrySet()) {
            if (entry.getValue() != null) {
                lgth += entry.getValue().length();
            }
        }
        System.out.println("lgth = " + lgth);
        return lgth;
    }

    @Override
    public String getBodyContentType() {
        return super.getBodyContentType();
        /*return httpentity.getContentType().getValue();*/
    }

    @Override
    public byte[] getBody() throws AuthFailureError {
        Thread thread = new Thread() {
            @Override
            public void run() {
                super.run();
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                try {
                    bos.write(byteArray, 0, byteArray.length);
                    httpentity.writeTo(new CountingOutputStream(bos, fileLength,
                            mProgressListener));
                } catch (IOException e) {
                    VolleyLog.e("IOException writing to ByteArrayOutputStream");
                }
            }
        };
        thread.start();

        return /*bos.toByteArray()*/ byteArray;
    }

    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {

        try {
            String jsonString = new String(response.data,
                    HttpHeaderParser.parseCharset(response.headers));

            return Response.success(jsonString,
                    HttpHeaderParser.parseCacheHeaders(response));

        } catch (UnsupportedEncodingException e) {
            return Response.error(new ParseError(e));
        }
    }

    @Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

    public static class CountingOutputStream extends FilterOutputStream {
        private final FileUploadManager.IMultipartProgressListener progressListener;
        private long transferred;
        private long fileLength;
        private int lastProgress = 0;

        public CountingOutputStream(final OutputStream out, long fileLength,
                                    final FileUploadManager.IMultipartProgressListener listener) {
            super(out);
            this.fileLength = fileLength;
            this.progressListener = listener;
            this.transferred = 0;
        }

        public void write(byte[] buffer, int offset, int length) throws IOException {
            out.write(buffer, offset, length);
            if (progressListener != null) {
                this.transferred += length;
                int progress = (int) ((transferred * 1000.0f) / fileLength);
                if (lastProgress != progress) {
                    this.progressListener.transferred(this.transferred, progress);
                }
            }
        }

        public void write(int oneByte) throws IOException {
            out.write(oneByte);
            if (progressListener != null) {
                this.transferred++;
                int progress = (int) ((transferred * 1000.0f) / fileLength);
                if (lastProgress != progress) {
                    this.progressListener.transferred(this.transferred, progress);
                }
            }
        }
    }

}

如您所见,我正在计算函数的进度 -> class CountingOutputStream 的写入。但我面临的问题是写函数调用太快,由于这个进度条更新很快,但即使在进度条完成后,成功上传文件的响应也需要很长时间。 我的猜测是写入功能很快,因此进度条已更新,但不知何故文件上传速度不快,因此完成进度后仍需要时间。

最后,经过几天的搜索,我发现这段代码工作正常。 如您所见,我在 headers 中添加了 Content-type、Content-Length 和主机。如果您的服务器不需要这些,您可以保留它们。特别是这个

((HttpURLConnection) urlconnection).setFixedLengthStreamingMode((int) fileInfo.fileSize);

line 帮助我实现了我想要实现的真实目标。它帮助我在服务器上写入数据,然后根据进度再将一些数据写入服务器,然后再次更新进度。所以这是最重要的一行。

public void main(FileInfo fileInfo, String put, String get, Message message, boolean isRetry) {
        URLConnection urlconnection = null;
        try {

            File file = new File(fileInfo.filePath);
            URL url = new URL(put);
            urlconnection = url.openConnection();
            urlconnection.setDoOutput(true);
            urlconnection.setDoInput(true);

            if (urlconnection instanceof HttpURLConnection) {
                ((HttpURLConnection) urlconnection).setRequestMethod("PUT");
                ((HttpURLConnection) urlconnection).setFixedLengthStreamingMode((int) fileInfo.fileSize);
                ((HttpURLConnection) urlconnection).setRequestProperty("Content-type", fileInfo.fileMimeType);
                ((HttpURLConnection) urlconnection).setRequestProperty("Content-length", String.valueOf(fileInfo.fileSize));
                ((HttpURLConnection) urlconnection).setRequestProperty("host", XMPP_SERVER);
                ((HttpURLConnection) urlconnection).connect();
            }

            BufferedOutputStream bos = new BufferedOutputStream(urlconnection.getOutputStream());
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

            long transferred = 0;
            int i = 0;
            byte[] buffer = new byte[4096];
            while ((i = bis.read(buffer)) > 0) {

                bos.write(buffer, 0, i);
                transferred += i;
                int progress = (int) ((transferred * 1000.0f) / fileInfo.fileSize);
                if (fileUploadListener != null) {
                    fileUploadListener.onProgressChanged(message, progress);

                }
            }


            bis.close();
            bos.close();

            InputStream inputStream;
            int responseCode = ((HttpURLConnection) urlconnection).getResponseCode();
            if ((responseCode >= 200) && (responseCode <= 202)) {

                inputStream = ((HttpURLConnection) urlconnection).getInputStream();
                int j;
                while ((j = inputStream.read()) > 0) {
                    System.out.println(j);
                }

                if (responseCode == 200) {
                    if (fileUploadListener != null)
                                           fileUploadListener.onAttachmentFileUpload(generateImageUploadResponse(false, get), message, isRetry);
                } else {

                    if (fileUploadListener != null)
                        fileUploadListener.onAttachmentFileUpload(generateImageUploadResponse(true, ""), message, isRetry);
                }
            } else {
                inputStream = ((HttpURLConnection) urlconnection).getErrorStream();

                if (fileUploadListener != null)
                    fileUploadListener.onAttachmentFileUpload(generateImageUploadResponse(true, ""), message, isRetry);
            }
            ((HttpURLConnection) urlconnection).disconnect();

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

            if (fileUploadListener != null)
                fileUploadListener.onAttachmentFileUpload(generateImageUploadResponse(true, ""), message, isRetry);
        }
    }