Android使用Volley框架定制PostUploadRequest上傳文件
發(fā)現(xiàn)問題
項(xiàng)目中有發(fā)表動(dòng)態(tài)的功能,該功能可以將文本和圖片上傳至服務(wù)器。
Volley通過定制PostUploadRequest實(shí)現(xiàn)文件上傳的功能,本文以一張圖片上傳為例。
數(shù)據(jù)格式
以下為項(xiàng)目中圖片上傳實(shí)例的數(shù)據(jù)格式
多張圖片上傳可通過添加——WebKitFormBoundary 內(nèi)容實(shí)現(xiàn)
POST /CloudLife/user/social HTTP/1.1 Host: localhost Connection: keep-alive Cache-Control: max-age=0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Origin: http://localhost User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryzayymBT8Owg2UzBR Referer: http://localhost/CloudLife/upload.jsp Accept-Encoding: gzip,deflate,sdch Accept-Language: zh-CN,zh;q=0.8 Cookie: CLOUD_LIFE=03F21B9A9D9B4FF2BF443290A9CD8E2C; USER=18060506304; JSESSIONID=C4AB532929FA43230FA193A98197F962 Content-Length: 12444 ------WebKitFormBoundaryzayymBT8Owg2UzBR Content-Disposition: form-data; name="text" 發(fā)表的圈子內(nèi)容 ------WebKitFormBoundaryzayymBT8Owg2UzBR Content-Disposition: form-data; name="file"; filename="這里是文件名" Content-Type: image/png 這里空一行 接下來是二進(jìn)制圖片文件內(nèi)容 ------WebKitFormBoundaryzayymBT8Owg2UzBR-- 這里為空白的一行
總共有加上結(jié)尾行,有五行,圖片的二進(jìn)制數(shù),整個(gè)算一行;下面來分析下:
1、第一行:”–” + boundary + “\r\n” ;
文件上傳在提交數(shù)據(jù)的開始標(biāo)志不變;
2、第二行:Content-Disposition: form-data; name=”參數(shù)的名稱”; filename=”上傳的文件名” + “\r\n”
3、第三行:Content-Type: 文件的 mime 類型 + “\r\n”
這一行是文件上傳必須要的,而普通的文字提交可有可無,mime 類型需要根據(jù)文檔查詢;
4、第四行:”\r\n”
5、第五行文件的二進(jìn)制數(shù)據(jù) + “\r\n”:
結(jié)尾行:”–” + boundary + “–” + “\r\n”
可以同時(shí)上傳多個(gè)文件,上傳多個(gè)文件的時(shí)候重復(fù)1、2、3、4、5步,在最后的一個(gè)文件的末尾加上統(tǒng)一的結(jié)束行。
上傳的圖像實(shí)體類
import java.io.ByteArrayOutputStream; import android.graphics.Bitmap; /* * 上傳的圖像實(shí)體類 * */ public class FormImage { // 參數(shù)的名稱 private String mName; // 文件名 private String mFileName; // 文件的 mime,需要根據(jù)文檔查詢 private String mMime; // 需要上傳的圖片資源,因?yàn)檫@里測(cè)試為了方便起見,直接把 bitmap 傳進(jìn)來,真正在項(xiàng)目中一般不會(huì)這般做, // 而是把圖片的路徑傳過來,在這里對(duì)圖片進(jìn)行二進(jìn)制轉(zhuǎn)換 private Bitmap mBitmap = null; public FormImage(Bitmap bitmap) { this.mBitmap = bitmap; } public String getName() { return "file"; } public String getFileName() { return "add.png"; } // 對(duì)圖片進(jìn)行二進(jìn)制轉(zhuǎn)換 public byte[] getValue() { ByteArrayOutputStream bos = new ByteArrayOutputStream(); mBitmap.compress(Bitmap.CompressFormat.JPEG, 80, bos); return bos.toByteArray(); } // 因?yàn)槲抑朗?png 文件,所以直接根據(jù)文檔查的 public String getMime() { return "image/png"; } }
定制PostUploadRequest
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.HashMap; import java.util.Map; import org.apache.http.protocol.HTTP; import org.json.JSONException; import org.json.JSONObject; import com.android.volley.AuthFailureError; import com.android.volley.DefaultRetryPolicy; import com.android.volley.NetworkResponse; import com.android.volley.ParseError; import com.android.volley.Request; import com.android.volley.Response; import com.android.volley.toolbox.HttpHeaderParser; import android.util.Log; /* * 發(fā)送文件的volley * post請(qǐng)求 Cookie * */ public class PostUploadRequest extends Request<JSONObject> { private Map<String, String> sendHeader = new HashMap<String, String>(); // 正確數(shù)據(jù)的時(shí)候回掉用 private Response.Listener<JSONObject> mListener; // 請(qǐng)求 數(shù)據(jù)通過參數(shù)的形式傳入 private String content; private FormImage mImage; // 數(shù)據(jù)分隔線 private String BOUNDARY = "----CloudLifeUpLoadImage"; private String MULTIPART_FORM_DATA = "multipart/form-data"; public PostUploadRequest(String url, String text, FormImage Item, Response.Listener<JSONObject> listener,Response.ErrorListener errorListener) { super(Method.POST, url, errorListener); this.mListener = listener; setShouldCache(false); mImage = Item; content = text; // 設(shè)置請(qǐng)求的響應(yīng)事件,因?yàn)槲募蟼餍枰^長(zhǎng)的時(shí)間,所以在這里加大了,設(shè)為5秒 setRetryPolicy(new DefaultRetryPolicy(5000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); } /* * 這里開始解析數(shù)據(jù) * @param response * Response from the network * @return * */ @Override protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) { try { // 防止中文亂碼 @SuppressWarnings("deprecation") String jsonString = new String(response.data, HTTP.UTF_8); JSONObject jsonObject = new JSONObject(jsonString); Log.w("upLoad", "jsonObject " + jsonObject.toString()); return Response.success(jsonObject, HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } catch (JSONException je) { return Response.error(new ParseError(je)); } } /* * 回調(diào)正確的數(shù)據(jù) * @param response * The parsed response returned by * */ @Override protected void deliverResponse(JSONObject response) { mListener.onResponse(response); } @Override public Map<String, String> getHeaders() throws AuthFailureError { return sendHeader; } public void setSendCookie(String cookie) { sendHeader.put("Cookie", cookie); } @Override public byte[] getBody() throws AuthFailureError { ByteArrayOutputStream bos = new ByteArrayOutputStream(); StringBuffer sb = new StringBuffer(); if (content == null) { /** * 圖片 */ /* 第一行 */ // `"--" + BOUNDARY + "\r\n"` sb.append("--" + BOUNDARY + "\r\n"); /* 第二行 */ // Content-Disposition: form-data; name="參數(shù)的名稱"; filename="上傳的文件名" + // "\r\n" sb.append("Content-Disposition: form-data;"); sb.append(" name=\""); sb.append(mImage.getName()); sb.append("\""); sb.append("; filename=\""); sb.append(mImage.getFileName()); sb.append("\""); sb.append("\r\n"); /* 第三行 */ // Content-Type: 文件的 mime 類型 + "\r\n" sb.append("Content-Type: "); sb.append(mImage.getMime()); sb.append("\r\n"); /* 第四行 */ // "\r\n" 空白的一行 sb.append("\r\n"); try { bos.write(sb.toString().getBytes("utf-8")); /* 第五行 */ // 文件的二進(jìn)制數(shù)據(jù) + "\r\n" bos.write(mImage.getValue()); bos.write("\r\n".getBytes("utf-8")); } catch (IOException e) { e.printStackTrace(); } /* 結(jié)尾行 */ // `"--" + BOUNDARY + "--" + "\r\n"` String endLine = "--" + BOUNDARY + "--" + "\r\n"; try { bos.write(endLine.toString().getBytes("utf-8")); } catch (IOException e) { e.printStackTrace(); } Log.v("upLoad", "=====formImage====\n" + bos.toString()); return bos.toByteArray(); } /** * 文字 */ /* 第一行 */ // `"--" + BOUNDARY + "\r\n"` sb.append("--" + BOUNDARY + "\r\n"); /* 第二行 */ // Content-Disposition: form-data; name="text" + "\r\n" sb.append("Content-Disposition: form-data;"); sb.append(" name=\""); sb.append("text"); sb.append("\""); sb.append("\r\n"); /* 第三行 */ // "\r\n" 空白的一行 sb.append("\r\n"); /* 第四行 */ // 文本內(nèi)容 sb.append(content); sb.append("\r\n"); if (mImage == null) { /* 結(jié)尾行 */ // `"--" + BOUNDARY + "--" + "\r\n"` String endLine = "--" + BOUNDARY + "--" + "\r\n"; try { bos.write(sb.toString().getBytes("utf-8")); bos.write(endLine.toString().getBytes("utf-8")); } catch (IOException e) { e.printStackTrace(); } Log.v("upLoad", "=====formImage====\n" + bos.toString()); return bos.toByteArray(); } else { /** * 圖片 */ /* 第一行 */ // `"--" + BOUNDARY + "\r\n"` sb.append("--" + BOUNDARY + "\r\n"); /* 第二行 */ // Content-Disposition: form-data; name="參數(shù)的名稱"; filename="上傳的文件名" + // "\r\n" sb.append("Content-Disposition: form-data;"); sb.append(" name=\""); sb.append(mImage.getName()); sb.append("\""); sb.append("; filename=\""); sb.append(mImage.getFileName()); sb.append("\""); sb.append("\r\n"); /* 第三行 */ // Content-Type: 文件的 mime 類型 + "\r\n" sb.append("Content-Type: "); sb.append(mImage.getMime()); sb.append("\r\n"); /* 第四行 */ // "\r\n" 空白的一行 sb.append("\r\n"); try { bos.write(sb.toString().getBytes("utf-8")); /* 第五行 */ // 文件的二進(jìn)制數(shù)據(jù) + "\r\n" bos.write(mImage.getValue()); bos.write("\r\n".getBytes("utf-8")); } catch (IOException e) { e.printStackTrace(); } } /* 結(jié)尾行 */ // `"--" + BOUNDARY + "--" + "\r\n"` String endLine = "--" + BOUNDARY + "--" + "\r\n"; try { bos.write(endLine.toString().getBytes("utf-8")); } catch (IOException e) { e.printStackTrace(); } Log.v("upLoad", "=====formImage====\n" + bos.toString()); return bos.toByteArray(); } // Content-Type: multipart/form-data; boundary=----------8888888888888 @Override public String getBodyContentType() { return MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY; } }
實(shí)例
RequestQueue mQueue = SingleRequestQueue.getRequestQueue(); FormImage image; if (imageUri != null) { Bitmap bitmap = null; try {// 將路徑轉(zhuǎn)化成bitmap bitmap=BitmapFactory.decodeStream( getContentResolver().openInputStream(imageUri)); } catch (FileNotFoundException e1) { e1.printStackTrace(); } image = new FormImage(bitmap); } else image = null; PostUploadRequest post = new PostUploadRequest(C.api.userIcon, null, image, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject jsonObject) { try { //TODO } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e("VolleyError", error.getMessage(), error); } }); if (!customer.getCookie().equals("")) { // 向服務(wù)器發(fā)起post請(qǐng)求時(shí)加上cookie字段 post.setSendCookie(customer.getCookie()); } mQueue.add(post);
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android框架Volley之利用Imageloader和NetWorkImageView加載圖片的方法
- Android框架Volley使用之Post請(qǐng)求實(shí)現(xiàn)方法
- Android框架Volley使用之Json請(qǐng)求實(shí)現(xiàn)
- Android框架Volley使用:ImageRequest請(qǐng)求實(shí)現(xiàn)圖片加載
- Android中volley封裝實(shí)踐記錄(二)
- Android中volley封裝實(shí)踐記錄
- Android Volley擴(kuò)展實(shí)現(xiàn)支持進(jìn)度條的文件上傳功能
- Android使用Volley實(shí)現(xiàn)上傳文件功能
- 解析Android框架之Volley源碼
相關(guān)文章
Android使用Intent的Action和Data屬性實(shí)現(xiàn)點(diǎn)擊按鈕跳轉(zhuǎn)到撥打電話和發(fā)送短信界面
這篇文章主要介紹了Android中使用Intent的Action和Data屬性實(shí)現(xiàn)點(diǎn)擊按鈕跳轉(zhuǎn)到撥打電話和發(fā)送短信,需要的朋友可以參考下2020-01-01Android學(xué)習(xí)教程之日歷控件使用(7)
這篇文章主要為大家詳細(xì)介紹了Android學(xué)習(xí)教程之日歷控件操作代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11Android如何調(diào)整線程調(diào)用棧大小
這篇文章主要介紹了Android如何調(diào)整線程調(diào)用棧大小,幫助大家更好的進(jìn)行Android開發(fā),完善自身程序,感興趣的朋友可以了解下2020-10-10Android個(gè)人手機(jī)通訊錄開發(fā)詳解
在本篇文章里小編給大家分享了關(guān)于Android個(gè)人手機(jī)通訊錄開發(fā)的步驟和相關(guān)源碼,有需要的朋友們學(xué)習(xí)下。2019-02-02Android 基于RecyclerView實(shí)現(xiàn)的歌詞滾動(dòng)自定義控件
這篇文章主要介紹了Android 基于RecyclerView實(shí)現(xiàn)的歌詞滾動(dòng)自定義控件,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-03-03Android實(shí)現(xiàn)多級(jí)樹形選擇列表
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多級(jí)樹形選擇列表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04Android 在程序運(yùn)行時(shí)申請(qǐng)權(quán)限的實(shí)例講解
下面小編就為大家分享一篇Android 在程序運(yùn)行時(shí)申請(qǐng)權(quán)限的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01Android TV開發(fā):使用RecycleView實(shí)現(xiàn)橫向的Listview并響應(yīng)點(diǎn)擊事件的代碼
這篇文章主要介紹了Android TV開發(fā):使用RecycleView實(shí)現(xiàn)橫向的Listview并響應(yīng)點(diǎn)擊事件的代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,一起跟隨小編過來看看吧2018-05-05Android在一個(gè)app中安裝并卸載另一個(gè)app的示例代碼
這篇文章主要介紹了Android在一個(gè)app中安裝并卸載另一個(gè)app的示例代碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03