Android network programming (6) Volley combat, upload files to Tencent Cloud

We briefly introduced the basic use of Volley for GET and POST for network requests in the previous blog post "Android Network Programming (5) Volley Framework Usage" Today we further explore how to use Volley to upload files.

1 Form structure

File upload is actually to submit the form, but there is a certain field in the form submission is the binary value of the file. Let's take Tencent Cloud upload API access to explain the use of Volley upload. Before we start, let's take a look at the data format submitted by the form. The following is an example packet capture data uploaded by Tencent Cloud:

POST /files/v2/<向腾讯云申请的appid>/<appid里头对应的bucket_name>/[文件夹]/<文件名> HTTP/1.1
Accept: */*
Authorization: XXX这里是腾讯云appid等一系列值生成的签名XXX
Content-Type: multipart/form-data;boundary=------------------------------分界字符
User-Agent: Dalvik/2.1.0 (Linux; U; Android 5.0.2; MI2 MIUI/6.10.20)
Host: gz.file.myqcloud.com
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 778
 
--------------------------------分界字符
Content-Disposition: form-data;name="insertOnly"
 
0
--------------------------------分界字符
Content-Disposition: form-data; name="op"
 
upload
--------------------------------分界字符
Content-Disposition: form-data; name="sha"
 
XXX这里是文件的SHA-1值XXX
--------------------------------分界字符
Content-Disposition: form-data;name="biz_attr"
 
 
--------------------------------分界字符
Content-Disposition: form-data;name="filecontent"
Content-Type: text/plain
 
XXX这里是文件转二进制的byte[]值XXX
--------------------------------分界字符—

Let's analyze the above data packet. It is divided into two parts. Lines 1 to 9 are headers, and lines 10 to the end are body (committed data). In Volley, most of the headers are generally automatically generated, except for a few points to be specified. For example, the line between POST and HTTP / 1.1 on the first line is the submitted URL; the Content-Type on line 4 specifies the type of the submitted parameter, Behind the boundary is the custom delimiter character, which must be consistent with the following; Tencent Cloud also needs to add signature verification, that is, the third line. Next, continue to analyze the body part. All the data of the body part is generated by manual stitching. It is gratifying that they are all regular.

 

2 UploadRequest

We introduced the use of StringRequest , JsonRequest, and ImageRequest in "Use of Volley Framework for Android Network Programming (5)" . They all inherit from the Request class, so we can imitate them and write an UploadRequest.

Create a new UploadRequest class, as follows:

public class UploadRequest extends Request<JSONObject> {

    private final static int TIME_OUT = 5 *60 * 1000;                                                  // 5分钟

    private Map<String,Object> mParams;
    private byte[] mFileByte;
    private String mAuthorization;
    private finalResponse.Listener<JSONObject> mListener;

    public UploadRequest(String url, Map<String, Object> params, String authorization, byte[]fileByte, Response.Listener<JSONObject> listener, Response.ErrorListenererrorListener) {
        super(Request.Method.POST,url, errorListener);
        mParams =params;
        mAuthorization= authorization;
        mFileByte =fileByte;
        this.mListener= listener;

        setShouldCache(false);
        setRetryPolicy(newDefaultRetryPolicy(TIME_OUT,DefaultRetryPolicy.DEFAULT_MAX_RETRIES,DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
    }

    @Override
    protected Response<JSONObject>parseNetworkResponse(NetworkResponse response) {
        try {
            String je = newString(response.data,HttpHeaderParser.parseCharset(response.headers,"utf-8"));
            return Response.success(newJSONObject(je), HttpHeaderParser.parseCacheHeaders(response));
        } catch (UnsupportedEncodingException var3) {
            return Response.error(newParseError(var3));
        } catch (JSONException var4) {
            return Response.error(newParseError(var4));
        }
    }

    @Override
    protected void deliverResponse(JSONObject jsonObject) {
        if(mListener != null){
            mListener.onResponse(jsonObject);
        }
    }
}

The first step is to make the UploadRequest class inherit Request. Because Tencent Cloud returns JSON type data after uploading, it is specified as JSONObject type;

The second step is to define the constructor. The constructor receives the requested url, parameters, Tencent Cloud signature, binary value of the file to be uploaded, upload success callback and upload failure callback. In the function entity, Volley's cache is set to false and the request timeout time is set here to 5 minutes. Of course, these two values ​​can also be passed in from the constructor to specify;

The third step is to rewrite the necessary two methods parseNetworkResponse and deliverResponse, parseNetworkResponse we can copy the parseNetworkResponse method in the JsonRequest class, deliverResponse is the callback that specifies the successful upload;

Next, continue to rewrite three very important methods, they are getBodyContentType, getHeaders and getBody:

private final static String BOUNDARY = "------------------------------lyz_zyx";   // 数据分界线

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

@Override
public Map<String, String> getHeaders() throwsAuthFailureError {
    Map<String, String> headers = newHashMap<>();
    headers.putAll(super.getHeaders());
   if (mAuthorization!= null && mAuthorization.isEmpty()){
        headers.put("Authorization",mAuthorization);
    }
    headers.put("Accept","*/*");             //这句一定要加上,否则上传的腾讯云上的文件会是0B,若加sha1校验,则上传失败
    return headers;
}

@Override
public byte[] getBody() throwsAuthFailureError {
    if (mParams== null) {
        return null;
    }
    ByteArrayOutputStream bos = newByteArrayOutputStream() ;
    String enterNewline = "\r\n";
    String fix = "--";
    StringBuffer sb= newStringBuffer() ;
    try {
        Iterator iter = mParams.entrySet().iterator();
        while (iter.hasNext()){
            Map.Entry entry = (Map.Entry)iter.next();
            Object key = entry.getKey();
            Object val =entry.getValue();

            sb.append(fix + BOUNDARY+ enterNewline);
            sb.append("Content-Disposition:form-data; name=\"" +key + "\"" +enterNewline);
            sb.append(enterNewline);
            sb.append(val +enterNewline);
        }
        sb.append(fix + BOUNDARY+ enterNewline);
        sb.append("Content-Disposition:form-data; name=\"filecontent\""+ enterNewline);
        sb.append("Content-Type:text/plain" + enterNewline);
        sb.append(enterNewline);

        bos.write(sb.toString().getBytes("utf-8"));
        bos.write(mFileByte);

        StringBuffer sbEnd= newStringBuffer() ;
        sbEnd.append(enterNewline) ;
        sbEnd.append(fix + BOUNDARY+ fix + enterNewline);

        bos.write(sbEnd.toString().getBytes("utf-8"));
    } catch (IOExceptione) {
        e.printStackTrace();
    }

    return bos.toByteArray();
}

The string returned by the getBodyContentType method is the fourth line of the headers we mentioned above;

The getHeaders method adds two values, Accept and Authorization. That is what we mentioned above in the header line 2 and line 3;

The getBody method is the body part we mentioned above that needs to be spliced ​​manually. Before it, a loop for the mParams value passed in by the constructor is made. After the loop is completed, the mFileByte part passed in by the constructor is connected.

The entire UploadRequest class is now complete. Although our original intention was to design for the upload function of Tencent Cloud, in fact, the UploadRequest class is also completely common to other ordinary forms to submit uploaded files, as long as no signature is passed in.

Finally, how to call:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button btn1 =(Button)findViewById(R.id.btn1);
    btn1.setOnClickListener(newView.OnClickListener() {
        @Override
        public void onClick(Viewv) {
            uploadFile("/sdcard/upload_log.zip");
        }
    });
}

/**
 * 执行上传文件
 * @param filePath
 */
private void uploadFile(String filePath) {
    File file = newFile(filePath);
    if (!file.exists()){
        return;
    }
    UploadFileData uploadFileData = newUploadFileData();
   uploadFileData.setFileName(file.getName());
    Common.getFileByteAndSha1(file,uploadFileData);

    String url = String.format(REQUEST_UPLOAD_URL,uploadFileData.getFileName());

    Map<String, Object> params = newHashMap<>();
    params.put("op","upload");
    params.put("sha",uploadFileData.getFileSha1().toLowerCase()); // 注意,sha1值必须小写,这点腾讯云API没提到
    params.put("biz_attr","");
    params.put("insertOnly",0);

    String authorization = getSign();

    RequestQueue requestQueue = Volley.newRequestQueue(getApplicationContext());
    UploadRequest uploadRequest = new UploadRequest(url, params, authorization,uploadFileData.getFileByte(),
            new Response.Listener<JSONObject>(){
                @Override
                publicvoid onResponse(JSONObject jsonObject) {
                    //TODO 上传成功
                }
            },
            new Response.ErrorListener(){
                @Override
                publicvoid onErrorResponse(VolleyError volleyError) {
                    //TODO 上传失败
                }
            });
    requestQueue.add(uploadRequest);
}

Corresponding code, the calling process is to first specify a mobile file directory, then correspond to the file and calculate its sha1 value and binary value, then calculate the uploaded signature through the information provided by Tencent Cloud, the last step is to construct an UploadRequest that we have prepared The object of the class is then handed over to RequestQueue. Another point, do n’t forget to declare the corresponding permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

3 Summary

This blog post mainly describes the structure of the form and how to use Volley to generate the form to request the network. Regarding the use of Tencent Cloud, in our example, in order to parse the submission of the form, the most original method of use is given. If your project does not mind the size of the package, you can use the Android SDK package provided by Tencent Cloud API. Just simply reference its jar in the project, the corresponding API introduction method can be very simple. Ten lines of code can complete the entire The upload function will not be introduced here in detail. For details, see Tencent's API https://www.qcloud.com/document/api/436/6066 . In addition, the complete source code of this project can be downloaded here .

 

Published 106 original articles · praised 37 · 80,000 views

Guess you like

Origin blog.csdn.net/lyz_zyx/article/details/73089230