[Android] 如何發送包含File data的POST Request(以Image Upload為例)


(How to build a multipart POST request)

Multipart POST是一種可傳送包含binary data的POST method,網頁中最常見的例子就是帶有附加檔案的Form,以下範例將說明在Android中如何透過HttpClient發送一個含有Image與字串的multipart POST。


程式碼說明

1. 下載HttpMIME Library

雖然Android有把HttpClient納入framework,但不包含HttpMIME,有了HttpMIME lib我們才可以進行multipart POST。

網址:http://hc.apache.org/downloads.cgi

下載後將.jar加入Project library(Project右鍵→Properties→Java Build Path→Add External JAR)



2. 定義程式Layout

這裡會定義一個簡單的form,包含一個TextView表示String data,另一個TextView代表attached file path,當User按下submit按鈕將會將form data透過multipart post將data送至後端(檔案的部份為了Demo方便所以以寫死的file path表示,其實可以再加入file browse的功能)。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:orientation="vertical" 
 android:gravity="center_horizontal"
>
 <LinearLayout 
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
 <TextView
  android:layout_width="130dp"
  android:layout_height="wrap_content"
  android:text="file Description:"/>
 <EditText
  android:id="@+id/desTxt" 
  android:layout_width="160dp"
  android:layout_height="wrap_content"
 />
 </LinearLayout>
 <LinearLayout 
  android:layout_width="match_parent"
  android:layout_height="wrap_content">
 <TextView
  android:layout_width="130dp"
  android:layout_height="wrap_content"
  android:text="File path:"/>
 <EditText
  android:id="@+id/filePath" 
  android:layout_width="160dp"
  android:layout_height="wrap_content"
 />
 </LinearLayout>
 <Button 
  android:id="@+id/submitBtn"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="Submit"
  android:onClick="onSubmit"    
 />
</LinearLayout>
3. 實作multipart post function

以HttpClient進行POST呼叫,並透過MultipartEntity加入欲傳遞的data,data大致分兩種類型String part與File part,都透過addPart方法附加參數至POST Request,最後再透過ResponseHandler取得Server端回應。
private void doMultiPost(String url, List< NameValuePair> params){
 HttpClient client=new DefaultHttpClient();
 HttpPost post=new HttpPost(url);
 try{
  //setup multipart entity
  MultipartEntity entity=new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
      
  for(int i=0;i< params.size();i++){
  //identify param type by Key
  if(params.get(i).getName().equals("file")){
   File f=new File(params.get(i).getValue());
   FileBody fileBody=new FileBody(f);
   entity.addPart("image"+i,fileBody);
  }else{
   entity.addPart(params.get(i).getName(),new StringBody(params.get(i).getValue(), ContentType.TEXT_PLAIN));
  }
  }
      
  post.setEntity(entity);
      
  //create response handler
  ResponseHandler< String> handler=new BasicResponseHandler();
  //execute and get response
  String response=new String(client.execute(post,handler).getBytes(),HTTP.UTF_8);
  Toast.makeText(this, response, Toast.LENGTH_SHORT).show();
  }catch(Exception e){
   e.printStackTrace();
  }     
}
4.Submit button click handler
public void onSubmit(View v){
 String path=((EditText)findViewById(R.id.filePath)).getText().toString();
 String des=((EditText)findViewById(R.id.desTxt)).getText().toString();
     
 List< NameValuePair> params = new ArrayList< NameValuePair>();
 params.add(new BasicNameValuePair("file",path));
 params.add(new BasicNameValuePair("des",des));
     
 doMultiPost("yourhost/MultipartPost.php",params);
}
4. 後端程式碼(以PHP為例)
//get http request parameters
    
if(move_uploaded_file($_FILES['image0']['tmp_name'], $_FILES['image0']['name'])){
 print_r("File".$_FILES['image0']['name']." is valid, and was successfully uploaded");
}else{
 print_r('Pic:'.$_FILES['image0']['name'].' Uploaded unsuccessfully!');
}

留言

  1. 請教一下,php端如果要存圖的話,就直接針對$image0作處理嗎?

    回覆刪除
  2. 不好意思,之前貼到另一個檔案的內容,新更新的後端程式就示範了如何儲存上傳後的檔案

    回覆刪除
  3. 很實用的文章,感謝!

    之前也用這個方法,在傳中文vars的時候會遇到亂碼。
    提供一個小建議:
    在第15行的:entity.addPart(params.get(i).getName(),new StringBody(params.get(i).getValue()));

    更改為:
    entity.addPart(params.get(i).getName(),new StringBody(params.get(i).getValue(), Charset.forName("UTF-8")));

    強制採UTF-8來傳vars就不會有亂碼了。
    也提供其它朋友參考。

    回覆刪除
  4. 謝謝cupid大的補充,本來的寫法的確會造成中文亂碼的問題,已更新至文章中,感謝!

    回覆刪除
  5. 感謝你分享這篇文章,幫助了我很多!

    回覆刪除
  6. @Cryus 不客氣,我只是簡單分享自己的經驗,能幫助到你,我覺得很棒!

    回覆刪除
  7. 請教一下,我在new Strinbody的時候會有刪除線,是什麼原因呢?

    回覆刪除
  8. @陳禹凡 原因在於 Stringbody(String text, Charset charset) 已經被 deprecated 了,請使用 Stringbody(String text, ContentTypes contentType),例如:new Stringbody(params.get(i).getValue(), ContentType.TEXT_PLAIN),文件請參考 https://hc.apache.org/httpcomponents-client-ga/httpmime/apidocs/org/apache/http/entity/mime/content/StringBody.html

    回覆刪除

張貼留言

這個網誌中的熱門文章

[Android] layout_weight的妙用-讓View的大小以百分比率顯示(proportionate size)

[Android] 內部儲存體(Internal Storage)的檔案系統讀寫(File I/O)

【海外婚紗】造型篇-我的超人新祕Sunny-Yang