okhttp post 上傳文件以及Multipart/form-data 的學習理解

在app 中,最基本的設置用戶頭像,需要進行頭像的圖片上傳,那就會使用到 post 進行上傳文件的操作。必然要了解下 Multipart/form-data 的含義。以前在寫這部分代碼的時候,不理解Headers.of(xxx)什麼意思,而且 裏面填寫的格式又有什麼要求代表了什麼意思 等。

這裏寫圖片描述

看到上面的api 的說明文檔更是頭大,所以需要了解下 Multipart/form-data

1.Multipart/form-data 解釋

  • 1.1 什麼是 Multipart/form-data:
    Multipart/form-data是上傳文件的一種方式,是瀏覽器用表單上傳文件的方式

  • 1.2 Multipart/form-data 上傳步驟:
    首先,客戶端和服務器建立連接(TCP協議)。
    第二,客戶端可以向服務器端發送數據。因爲上傳文件實質上也是向服務器端發送請求。
    第三,客戶端按照符合“multipart/form-data”的格式向服務器端發送數據。(這一點非常重要)。

  • 1.3 Multipart/form-data 上傳格式的說明:

因爲在進行上傳文件的操作的時候,有些公共的api 會要求header 的上傳格式 ,所以需要具體瞭解下相關格式的含義,以便更加精確的瞭解。下面是摘錄的一段(http://blog.csdn.net/xiaojianpitt/article/details/6856536

這裏是相關請求頭的信息:

POST /t2/upload.do HTTP/1.1
User-Agent: SOHUWapRebot
Accept-Language: zh-cn,zh;q=0.5
Accept-Charset: GBK,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Content-Length: 60408
Content-Type:multipart/form-data; boundary=ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Host: w.sohu.com

–ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name=”desc”
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

[……][……][……][……]………………………
–ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC
Content-Disposition: form-data;name=”pic”; filename=”photo.jpg”
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary

[圖片二進制數據]
–ZnGpDtePMx0KrHh_G0X99Yef9r8JZsRJSXC–

我們來分析下數據,第一個空行之前自然還是HTTP header,之後則是Entity,而此時的Entity也比之前要複雜一些。根據RFC 1867定義,我們需要選擇一段數據作爲“分割邊界”( boundary屬性),這個“邊界數據”不能在內容其他地方出現,一般來說使用一段從概率上說“幾乎不可能”的數據即可。 不同瀏覽器的實現不同,例如火狐某次post的 boundary=—————————32404670520626 , opera爲boundary=———-E4SgDZXhJMgNE8jpwNdOAX ,每次post瀏覽器都會生成一個隨機的30-40位長度的隨機字符串,瀏覽器一般不會遍歷這次post的所有數據找到一個不可能出現在數據中的字符串,這樣代價太大了。一般都是隨機生成,如果你遇見boundary值和post的內容一樣,那樣的話這次上傳肯定失敗,不過我建議你去買彩票,你太幸運了。Rfc1867這樣說明{A boundary is selected that does not occur in any of the data. (This selection is sometimes done probabilisticly.)}。

選擇了這個邊界之後,瀏覽器便把它放在Content-Type 裏面傳遞給服務器,服務器根據此邊界解析數據。下面的數據便根據boundary劃分段,每一段便是一項數據。(每個field被分成小部分,而且包含一個value是”form-data”的”Content-Disposition”的頭部;一個”name”屬性對應field的ID,等等,文件的話包括一個filename)
IE和Chrome在filename的選擇策略上有所不同,前者是文件的完整路徑,而後者則僅僅是文件名。
數據內容以兩條橫線結尾,並同樣以一個換行結束。在網絡協議中一般都以連續的CR、LF(即\r、\n,或0x0D、Ox0A)字符作爲換行,這與Windows的標準一致。如果您使用其他操作系統,則需要考慮它們的換行符。
另外Content-length 指的是所用數據的長度。

2.okhttp post 上傳圖片文件

public class UploadHelper extends Presenter {

    public static final String TAG = "UploadHelper";
    private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
    private final OkHttpClient client = new OkHttpClient();

    public String upload(String imageType,String userPhone,File file) throws NetworkException{

        RequestBody fileBody = RequestBody.create(MediaType.parse("image/png"), file);

        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
///                .addPart(
//                        Headers.of("Content-Disposition", "form-data; name=\"file\"; filename=\"" + fileName + "\""),
//                        RequestBody.create(MEDIA_TYPE_PNG, file))
//                .addPart(
//                        Headers.of("Content-Disposition", "form-data; name=\"imagetype\""),
//                        RequestBody.create(null, imageType))
//                .addPart(
//                        Headers.of("Content-Disposition", "form-data; name=\"userphone\""),
//                        RequestBody.create(null, userPhone))

                .addFormDataPart("file", "head_image", fileBody)
                .addFormDataPart("imagetype", imageType)
                .addFormDataPart("userphone", userPhone)
                .build();

        Request request = new Request.Builder()
                .url("http://xxxxx")
                .post(requestBody)
                .build();

        Response response;
        try {
            response = client.newCall(request).execute();
            String jsonString = response.body().string();
            Log.d(TAG," upload jsonString ="+jsonString);

            if(!response.isSuccessful()){
                throw new NetworkException("upload error code "+response);
            }else{
                JSONObject jsonObject = new JSONObject(jsonString);
                int errorCode = jsonObject.getInt("errorCode");
                if(errorCode == 0){
                    Log.d(TAG," upload data ="+jsonObject.getString("data"));
                    return jsonObject.getString("data");
                }else {
                    throw new NetworkException("upload error code "+errorCode+",errorInfo="+jsonObject.getString("errorInfo"));
                }
            }

        } catch (IOException e) {
            Log.d(TAG,"upload IOException ",e);
        }catch (JSONException e){
            Log.d(TAG,"upload JSONException ",e);
        }
        return null;
    }

addPart 就是發送頭部的具體內容了,其中 addFormDataPart 封裝了部分內容,本質上 和

.addPart(
Headers.of(“Content-Disposition”, “form-data; name=\”token\”“),
RequestBody.create(null, uploadToken))

道理是一樣的,只是內部進行了自己的封裝。下面是相關的源代碼

這裏寫圖片描述

3.相關的參考的博客

http://blog.csdn.net/xiaojianpitt/article/details/6856536

http://blog.csdn.net/lmj623565791/article/details/23781773

http://blog.csdn.net/lmj623565791/article/details/47911083

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章