volley实现multi part上传

老古董volley,相比retrofit等新秀来说学习曲线平缓,所以项目为了快速上线就选择了它。有一个需求是通过一个请求上传多张图片到服务器,很快就到gayhub上找到了别人造好的轮子,代码如下:

/**
 * Custom request to make multipart header and upload file.
 *
 * Sketch Project Studio
 * Created by Angga on 27/04/2016 12.05.
 */
public class VolleyMultipartRequest extends Request {
    private final String twoHyphens = "--";
    private final String lineEnd = "\r\n";
    private final String boundary = "apiclient-" + System.currentTimeMillis();

    private Response.Listener mListener;
    private Response.ErrorListener mErrorListener;
    private Map 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 VolleyMultipartRequest(String url, Map headers,
                                  Response.Listener 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 VolleyMultipartRequest(int method, String url,
                                  Response.Listener listener,
                                  Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.mListener = listener;
        this.mErrorListener = errorListener;
    }

    ... ...

    @Override
    protected Response 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 params, String encoding) throws IOException {
        try {
            for (Map.Entry 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 data) throws IOException {
        for (Map.Entry 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);
    }

    ... ...
}

整个类太长只截取一部分,使用中遇到的问题是,当上传文件的时候同时带有其他参数而且参数中有中文字符串的时候,到了服务器就是乱码而英文没有问题。最初怀疑是accepted这个header设置的问题,改成utf-8仍然有问题,如果参数用utf-8先encode一遍则服务器也要做相应的修改,先decode再入库。因为iOS端没问题,所以没有考虑后一个方案。

其他接口使用中文参数是没问题的,所以单独考虑这个上传request的代码。在处理函数的时候,这个类使用的是

dataOutputStream.writeBytes(parameterValue + lineEnd);

直接writeBytes,因为java里边的字符是双字节的,所以如果用这个参数的话高8位会被丢弃。改为如下语句解决问题:

dataOutputStream.write(parameterValue.getBytes());
dataOutputStream.writeBytes(lineEnd);

gayhub上搜索发现很多库直接用了这个文件,可能只是一直使用英文参数没有出现问题,相当于埋了一颗雷。如果想要该类的完整代码,直接搜索VolleyMultipartRequest.java应该能搜很多。

你可能感兴趣的:(volley实现multi part上传)