在Android中使用Volley将图片分段上传到服务器

时间:2019-04-04 11:31:29

标签: android image file-upload android-volley multipartform-data

如何在Android中使用Volley分段上传图像,并且如果网络出现暂停/故障,如何从停止点重新开始上传?

我尝试了使用分段上传图片的代码。它未经测试,所以我不知道它是否有效。我不知道第二部分是否可以实现。如果是,那怎么办?

MultipartRequest.java-

public class MultipartRequest extends Request<NetworkResponse> {
private final String twoHyphens = "--";
private final String lineEnd = "\r\n";
private final String boundary = "apiclient-" + System.currentTimeMillis();

private Response.Listener<NetworkResponse> mListener;
private Response.ErrorListener mErrorListener;
private Map<String, String> mHeaders;

/**
 * Default constructor with predefined header and post method.
 *
 * @param url           request destination
 * @param headers       predefined custom header
 * @param listener      on success achieved 200 code from request
 * @param errorListener on error http or library timeout
 */
public MultipartRequest(String url, Map<String, String> headers,
                              Response.Listener<NetworkResponse> listener,
                              Response.ErrorListener errorListener) {
    super(Method.POST, url, errorListener);
    this.mListener = listener;
    this.mErrorListener = errorListener;
    this.mHeaders = headers;
}

/**
 * Constructor with option method and default header configuration.
 *
 * @param method        method for now accept POST and GET only
 * @param url           request destination
 * @param listener      on success event handler
 * @param errorListener on error event handler
 */
public MultipartRequest(int method, String url,
                              Response.Listener<NetworkResponse> listener,
                              Response.ErrorListener errorListener) {
    super(method, url, errorListener);
    this.mListener = listener;
    this.mErrorListener = errorListener;
}

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    return (mHeaders != null) ? mHeaders : super.getHeaders();
}

@Override
public String getBodyContentType() {
    return "multipart/form-data;boundary=" + boundary;
}

@Override
public byte[] getBody() throws AuthFailureError {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    DataOutputStream dos = new DataOutputStream(bos);

    try {
        // populate text payload
        Map<String, String> params = getParams();
        if (params != null && params.size() > 0) {
            textParse(dos, params, getParamsEncoding());
        }

        // populate data byte payload
        Map<String, DataPart> data = getByteData();
        if (data != null && data.size() > 0) {
            dataParse(dos, data);
        }

        // close multipart form data after text and file data
        dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        return bos.toByteArray();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}

/**
 * Custom method handle data payload.
 *
 * @return Map data part label with data byte
 * @throws AuthFailureError
 */
protected Map<String, DataPart> getByteData() throws AuthFailureError {
    return null;
}

@Override
protected Response<NetworkResponse> parseNetworkResponse(NetworkResponse response) {
    try {
        return Response.success(
                response,
                HttpHeaderParser.parseCacheHeaders(response));
    } catch (Exception e) {
        return Response.error(new ParseError(e));
    }
}

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

@Override
public void deliverError(VolleyError error) {
    mErrorListener.onErrorResponse(error);
}

/**
 * Parse string map into data output stream by key and value.
 *
 * @param dataOutputStream data output stream handle string parsing
 * @param params           string inputs collection
 * @param encoding         encode the inputs, default UTF-8
 * @throws IOException
 */
private void textParse(DataOutputStream dataOutputStream, Map<String, String> params, String encoding) throws IOException {
    try {
        for (Map.Entry<String, String> entry : params.entrySet()) {
            buildTextPart(dataOutputStream, entry.getKey(), entry.getValue());
        }
    } catch (UnsupportedEncodingException uee) {
        throw new RuntimeException("Encoding not supported: " + encoding, uee);
    }
}

/**
 * Parse data into data output stream.
 *
 * @param dataOutputStream data output stream handle file attachment
 * @param data             loop through data
 * @throws IOException
 */
private void dataParse(DataOutputStream dataOutputStream, Map<String, DataPart> data) throws IOException {
    for (Map.Entry<String, DataPart> entry : data.entrySet()) {
        buildDataPart(dataOutputStream, entry.getValue(), entry.getKey());
    }
}

/**
 * Write string data into header and data output stream.
 *
 * @param dataOutputStream data output stream handle string parsing
 * @param parameterName    name of input
 * @param parameterValue   value of input
 * @throws IOException
 */
private void buildTextPart(DataOutputStream dataOutputStream, String parameterName, String parameterValue) throws IOException {
    dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
    dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" + parameterName + "\"" + lineEnd);
    //dataOutputStream.writeBytes("Content-Type: text/plain; charset=UTF-8" + lineEnd);
    dataOutputStream.writeBytes(lineEnd);
    dataOutputStream.writeBytes(parameterValue + lineEnd);
}

/**
 * Write data file into header and data output stream.
 *
 * @param dataOutputStream data output stream handle data parsing
 * @param dataFile         data byte as DataPart from collection
 * @param inputName        name of data input
 * @throws IOException
 */
private void buildDataPart(DataOutputStream dataOutputStream, DataPart dataFile, String inputName) throws IOException {
    dataOutputStream.writeBytes(twoHyphens + boundary + lineEnd);
    dataOutputStream.writeBytes("Content-Disposition: form-data; name=\"" +
            inputName + "\"; filename=\"" + dataFile.getFileName() + "\"" + lineEnd);
    if (dataFile.getType() != null && !dataFile.getType().trim().isEmpty()) {
        dataOutputStream.writeBytes("Content-Type: " + dataFile.getType() + lineEnd);
    }
    dataOutputStream.writeBytes(lineEnd);

    ByteArrayInputStream fileInputStream = new ByteArrayInputStream(dataFile.getContent());
    int bytesAvailable = fileInputStream.available();

    int maxBufferSize = 1024 * 1024;
    int bufferSize = Math.min(bytesAvailable, maxBufferSize);
    byte[] buffer = new byte[bufferSize];

    int bytesRead = fileInputStream.read(buffer, 0, bufferSize);

    while (bytesRead > 0) {
        dataOutputStream.write(buffer, 0, bufferSize);
        bytesAvailable = fileInputStream.available();
        bufferSize = Math.min(bytesAvailable, maxBufferSize);
        bytesRead = fileInputStream.read(buffer, 0, bufferSize);
    }

    dataOutputStream.writeBytes(lineEnd);
}

/**
 * Simple data container use for passing byte file
 */
public class DataPart {
    private String fileName;
    private byte[] content;
    private String type;

    /**
     * Default data part
     */
    public DataPart() {
    }

    /**
     * Constructor with data.
     *
     * @param name label of data
     * @param data byte data
     */
    public DataPart(String name, byte[] data) {
        fileName = name;
        content = data;
    }

    /**
     * Constructor with mime data type.
     *
     * @param name     label of data
     * @param data     byte data
     * @param mimeType mime data like "image/jpeg"
     */
    public DataPart(String name, byte[] data, String mimeType) {
        fileName = name;
        content = data;
        type = mimeType;
    }

    /**
     * Getter file name.
     *
     * @return file name
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * Setter file name.
     *
     * @param fileName string file name
     */
    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    /**
     * Getter content.
     *
     * @return byte file data
     */
    public byte[] getContent() {
        return content;
    }

    /**
     * Setter content.
     *
     * @param content byte file data
     */
    public void setContent(byte[] content) {
        this.content = content;
    }

    /**
     * Getter mime type.
     *
     * @return mime type
     */
    public String getType() {
        return type;
    }

    /**
     * Setter mime type.
     *
     * @param type mime type
     */
    public void setType(String type) {
        this.type = type;
    }
  }
}

上传图片功能:

public static void uploadMultipartImage(final Bitmap bitmap, final 
UploadServiceRestInf restclientinterface, final Context context) {
    String url = "";
    MultipartRequest multipartRequest = new 
    MultipartRequest(Request.Method.POST, url, new 
Response.Listener<NetworkResponse>() {
        @Override
        public void onResponse(NetworkResponse response) {
            String resultResponse = new String(response.data);
            try {
                JSONObject result = new JSONObject(resultResponse);
                String status = result.getString("status");
                String message = result.getString("message");
                Log.e("UploadImageService", message);
                restclientinterface.uploadStatus(message);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            NetworkResponse networkResponse = error.networkResponse;
            String errorMessage = "Unknown error";
            if (networkResponse == null) {
                if (error.getClass().equals(TimeoutError.class)) {
                    errorMessage = "Request timeout";
                } else if (error.getClass().equals(NoConnectionError.class)) {
                    errorMessage = "Failed to connect server";
                }
            } else {
                String result = new String(networkResponse.data);
                try {
                    JSONObject response = new JSONObject(result);
                    String status = response.getString("status");
                    String message = response.getString("message");
                    Log.e("UploadImageService", "onErrorResponse : " + message);
                    restclientinterface.uploadStatus(message);

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

        }
    }) {

       @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> params = getHeaderParams(context);
            return params;
        }


        @Override
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<>();
            return params;
        }

        @Override
        protected Map<String, DataPart> getByteData() {
            Map<String, DataPart> params = new HashMap<>();
            // params.put("avatar", new DataPart("file_avatar.jpg", ImageUploadUtils.getFileDataFromBitmap(context,bitmap), "image/jpeg"));
            params.put("cover", new DataPart("file_cover.jpg", ImageUploadUtils.getFileDataFromBitmap(context, bitmap), "image/jpeg"));
            return params;
        }
    };
    queue = VolleySingleton.getInstance(context).getRequestQueue();
    queue.add(multipartRequest);
}

实用程序类:

/**
 * Turn bitmap into byte array.
 *
 * @param bitmap
 * @return byte array
 */
public static byte[] getFileDataFromBitmap(Context context, Bitmap bitmap) {
    ByteArrayOutputStream byteArrayOutputStream = new 
    ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, 80, byteArrayOutputStream);
    return byteArrayOutputStream.toByteArray();
    }

我不明白上面的代码是否正确?文件上传功能中的getByteData()getParams()在做什么?

如何从停止的部分继续上传图片?

0 个答案:

没有答案