以前のブログ投稿「Android Network Programming(5)Volley Framework Usage」で、GETとPOSTのネットワークリクエストに対する基本的な使い方を簡単に紹介しました。今日は、Volleyを使用してファイルをアップロードする方法をさらに探ります。
1フォーム構造
ファイルのアップロードは実際にはフォームを送信することですが、フォームの送信にはファイルのバイナリ値である特定のフィールドがあります。Tencent CloudアップロードAPIにアクセスして、Volleyアップロードの使用方法を説明しましょう。始める前に、フォームから送信されたデータ形式を見てみましょう。以下は、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
--------------------------------分界字符—
上記のデータパケットを分析してみましょう。2行に分かれています。1行目から9行目がヘッダー、10行目から最後までが本文(コミット済みデータ)です。Volleyでは、指定するいくつかの点を除いて、ほとんどのヘッダーが自動的に生成されます。たとえば、最初の行のPOSTとHTTP / 1.1の間の行は送信されたURLです。4行目のContent-Typeは送信されたパラメーターのタイプを指定します。境界の後ろにはカスタム区切り文字があり、これは以下と一致している必要があります。TencentCloudは、署名検証、つまり3行目も追加する必要があります。次に、身体部分の分析を続けます。身体部分のすべてのデータは手縫いによって生成されます。これらがすべて規則的であることに満足しています。
2 UploadRequest
私たちは「バレーボールフレームワークの使用してプログラミングAndroidのネットワーク(E)」で紹介しStringRequest、JSONRequestとImageRequestそれらはから継承されている使用、リクエスト我々はUploadRequestを書くためにそれらを模倣することができますので、クラス。
次のように、新しいUploadRequestクラスを作成します。
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);
}
}
}
最初のステップは、UploadRequestクラスにリクエストを継承させることです。TencentCloudはアップロード後にJSONタイプのデータを返すため、JSONObjectタイプとして指定されます。
2番目のステップは、コンストラクターを定義することです。コンストラクターは、要求されたURL、パラメーター、Tencent Cloudシグネチャ、アップロードされるファイルのバイナリ値、アップロード成功コールバック、アップロード失敗コールバックを受け取ります。関数エンティティでは、Volleyのキャッシュがfalseに設定され、リクエストのタイムアウト時間が5分に設定されています。もちろん、これらの2つの値をコンストラクターから渡して指定することもできます。
3番目のステップは、必要な2つのメソッドparseNetworkResponseとdeliverResponseを書き直すことです。parseNetworkResponseは、JsonRequestクラスのparseNetworkResponseメソッドをコピーできます。deliveryResponseは、正常なアップロードを指定するコールバックです。
次に、getBodyContentType、getHeaders、およびgetBodyの3つの非常に重要なメソッドを引き続き書き換えます。
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();
}
getBodyContentTypeメソッドによって返される文字列は、前述のヘッダーの4行目です。
getHeadersメソッドは、AcceptとAuthorizationの2つの値を追加します。これは、ヘッダー行の2行目と3行目で前述したものです。
getBodyメソッドは、前述のボディパーツであり、手動でスプライスする必要があります。その前に、コンストラクターによって渡されたmParams値のループが作成されます。ループが完了すると、コンストラクターによって渡されたmFileByteパーツが接続されます。
UploadRequestクラス全体が完成しました。最初の目的はTencent Cloudのアップロード機能用に設計することでしたが、実際、UploadRequestクラスは、署名が渡されない限り、アップロードされたファイルを送信する他の通常のフォームにも完全に共通です。
最後に、呼び出す方法:
@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);
}
対応するコード、呼び出しプロセスは、最初にモバイルファイルディレクトリを指定し、次にファイルに対応させ、そのsha1値とバイナリ値を計算し、Tencent Cloudから提供された情報を通じてアップロードされた署名を計算します。最後のステップは、準備したUploadRequestを作成することです。次に、クラスのオブジェクトがRequestQueueに渡されます。別のポイントとして、対応する権限を宣言することを忘れないでください。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
3まとめ
このブログ投稿では、主にフォームの構造と、Volleyを使用してフォームを生成し、ネットワークをリクエストする方法について説明します。Tencent Cloudの使用に関して、この例では、フォームの送信を解析するために、最も独創的な使用方法が示されています。プロジェクトがパッケージのサイズを気にしない場合は、Tencent Cloud APIによって提供されるAndroid SDKパッケージを使用できます。プロジェクトでそのjarを参照するだけで、対応するAPI導入メソッドは非常に簡単になります。10行のコードで全体を完了することができますアップロード機能の詳細はここでは紹介しません。詳細については、TencentのAPI https://www.qcloud.com/document/api/436/6066を参照してください。また、このプロジェクトの完全なソースコードは、こちらからダウンロードできます。