一、MultipartEntityBuilder 實現文件上傳步驟

在HttpCient4.3之前上傳文件主要使用MultipartEntity這個類,但現在這個類已經不在推薦使用了。隨之替代它的類是MultipartEntityBuilder。
二、MultipartEntityBuilder類介紹
MultipartEntityBuilder這個類主要用於創建HttpEntity。它的主要方法有:
飾符和類型 |
方法和描述 |
MultipartEntityBuilder |
addBinaryBody(String name, byte[] b) 將字節數組以二進制的形式添加數據。 |
MultipartEntityBuilder |
addBinaryBody(String name, byte[] b, ContentType contentType, String filename) 將字節數組以二進制的形式添加數據。 |
MultipartEntityBuilder |
addBinaryBody(String name, File file) 將文件以二進制的形式添加數據。 |
MultipartEntityBuilder |
addBinaryBody(String name, File file, ContentType contentType, String filename) 將文件以二進制的形式添加數據。 |
MultipartEntityBuilder |
addBinaryBody(String name, InputStream stream) |
MultipartEntityBuilder |
addBinaryBody(String name, InputStream stream, ContentType contentType, String filename) 將輸入流以二進制的形式添加數據。 |
MultipartEntityBuilder |
addPart(String name, ContentBody contentBody) 添加ContentBody 類型的數據。 |
MultipartEntityBuilder |
addTextBody(String name, String text) 添加文本數據。 |
MultipartEntityBuilder |
addTextBody(String name, String text, ContentType contentType) 以指定的內容類型添加文本數據。 |
HttpEntity |
build() 創建一個HttpEntity。 |
static MultipartEntityBuilder |
create() 創建一個MultipartEntityBuilder對象。 |
MultipartEntityBuilder |
setBoundary(String boundary) 設置邊界。 |
MultipartEntityBuilder |
setCharset(Charset charset) 設置請求的編碼格式。 |
MultipartEntityBuilder |
setLaxMode() |
MultipartEntityBuilder |
setMode(HttpMultipartMode mode) 設置模式。 |
MultipartEntityBuilder |
setStrictMode() |
主要方法說明:
addBinaryBody、addPart、addTextBody方法用於添加要上傳的數據,從上面的表格中可以發現用於添加數據的方法,都是key-value類型。所以在服務器端我們可以通過request.getPart("keyname")方式獲取對應key的數據。也可以通過request.getParts()方式獲取客戶端通過以上三種方法提交所有數據。
1.通過addBinaryBody方法直接可以添加File、InputStream、byte[]類型的數據。
2.通過addPart方法只能添加ContentBody類型的數據,在org.apache.http.entity.mime.content包中已經提供了String、File以及InputStream對應的ContentBody類型的子類,如FileBody、InputStreamBody、StringBody,通過這些類我們可以將String、File以及InputStream類型的數據轉換成ContentBody類型的數據。
3.通過addTextBody方法我們可以很方便的添加文本數據。
三、.通過HttpCient上傳文件核心代碼實現
Android端需要添加httpcore-4.3.2.jar、httpmime-4.3.5.jar兩個包。兩個包缺一不可。在這里我用的是最新版的HttpCient,大家可以從http://hc.apache.org/downloads.cgi上下載所需要的jar包,如果上面的網站打不開,大家也不用擔心,我已經將項目中所需要的jar包上傳到CSDN上《httpcomponents-client-4.3.5-bin.zip》需要的朋友可以去下載。
3.1 Android端項目核心代碼:
HttpClient client=new DefaultHttpClient();// 開啟一個客戶端 HTTP 請求 HttpPost post = new HttpPost(url);//創建 HTTP POST 請求 MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setCharset(Charset.forName("uft-8"));//設置請求的編碼格式 builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);//設置瀏覽器兼容模式 int count=0; for (File file:files) { builder.addBinaryBody("file"+count, file); count++; } builder.addTextBody("method", params.get("method"));//設置請求參數 builder.addTextBody("fileTypes", params.get("fileTypes"));//設置請求參數 HttpEntity entity = builder.build();// 生成 HTTP POST 實體 post.setEntity(entity);//設置請求參數 HttpResponse response = client.execute(post);// 發起請求 並返回請求的響應 if (response.getStatusLine().getStatusCode()==200) { return true; } return false;
代碼分析:
上面代碼主要實現了多文件上傳,為了方便服務器端保存文件,上面代碼設置了名稱為fileTypes的參數,fileTypes是由上傳的文件類型名拼接成的字符串,如”.jpg.png.docx“;
3.2 服務器端項目核心代碼:
服務器端可以通過獲取名為fileTypes的參數,然后將其拆分成字符數組,即可得到要保存文件的類型。服務器段主要用到Servlet3.0的API,主要用到的方法有:
1. request.getParameter("");//獲取客戶端通過addTextBody方法添加的String類型的數據。
2. request.getPart("");//獲取客戶端通過addBinaryBody、addPart、addTextBody方法添加的指定數據,返回Part類型的對象。
3. request.getParts();//獲取客戶端通過addBinaryBody、addPart、addTextBody方法添加的所有數據,返回Collection<Part>類型的對象。
4. part.getName();//獲取上傳文件的名稱即上傳時指定的key。
5. part.getSize()//獲取上傳文件的大小單位為字節。
String fileTypes=request.getParameter("fileTypes");//獲取客戶端上傳的所有文件類型 String[]typeArray=fileTypes.substring(1).split("\\.");//將文件類型字符串拆分成String數組 try { Iterator<Part>iterator=request.getParts().iterator(); int count=0; while (iterator.hasNext()) {//遍歷客戶端上傳的所有文件 if (count>=typeArray.length)break;//如果超出文件類型數組的大小則跳出循環 Part part = (Part) iterator.next(); System.out.println("part.getSize()"+part.getSize());//獲取上傳文件的大小 System.out.println("part.getName()"+part.getName());//獲取上傳文件的名及添加數據時的key名 File file=new File("E:\\upload\\"+count+"."+typeArray[count++]); InputStream inputStream=part.getInputStream(); FileOutputStream fos=new FileOutputStream(file); byte[]buffer=new byte[1024]; int len=0; while ((len=inputStream.read(buffer))!=-1) { fos.write(buffer,0, len); } inputStream.close(); fos.close(); } }catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }
代碼分析:
服務器端是通過Servlet實現的,通過調用request.getParameter("fileTypes")方法來獲取客戶端上傳的所有文件類型,然后將文件類型字符串拆分成String數組。通過request.getParts()方法取出客戶端通過addBinaryBody、addPart、addTextBody上傳的所有數據,然后遍歷數據集合即可進行文件的保存。
由於事先和客戶端協定,添加上傳文件的順序在添加請求參數之前,所以可以根據拆分出的文件類型數組的長度判斷出客戶端上傳文件的個數,因此當上面代碼遍歷超出了類型數組的長度時程序跳出循環,不再進行文件的保存,因為下面的Part都是些參數,而不是要保存的文件了。
3.3 程序運行效果圖:
四、項目代碼
MainActivity.java
package com.jph.ufh.activity; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import com.jph.ufh.R; import com.jph.ufh.service.UploadService; import android.app.Activity; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.widget.Toast; /** * 通過httpClient批量上傳文件 * @author jph * Date:2014.10.09 */ public class MainActivity extends Activity { private ArrayList<File>files; private Map<String, String>params; Handler mHandler=new Handler(){ @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub switch (msg.what) { case UploadService.UPLOAD_SUCCESS: Toast.makeText(MainActivity.this, "上傳成功", Toast.LENGTH_LONG).show(); break; } super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); files=new ArrayList<File>(); params=new HashMap<String, String>(); } public void upload(View v) { files.clear(); params.clear(); File file=new File(Environment.getExternalStorageDirectory(),"kaola.jpg"); File file2=new File(Environment.getExternalStorageDirectory(),"test.docx"); File file3=new File(Environment.getExternalStorageDirectory(),"test.jpg"); files.add(file); files.add(file2); files.add(file3); StringBuffer sbFileTypes=new StringBuffer(); for (File tempFile:files) { String fileName=tempFile.getName(); sbFileTypes.append(getFileType(fileName)); } params.put("fileTypes",sbFileTypes.toString()); params.put("method", "upload"); UploadService uploadService=new UploadService(mHandler); uploadService.uploadFileToServer(params, files); } /** * 獲取文件的類型 * @param fileName :文件名 * @return 文件類型 */ private String getFileType(String fileName) { // TODO Auto-generated method stub return fileName.substring(fileName.lastIndexOf("."), fileName.length()); } }
UploadService.java
package com.jph.ufh.service; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.DefaultHttpClient; import android.os.Handler; /** * 采用HttpClient上傳文件,支持多文件上傳 * @author jph * Date:2014.10.09 */ public class UploadService { private static String url="http://10.219.57.16:8080/ServerForUpload/ServletForUpload"; // private static String url="http://10.110.6.58:8080/ServerForUpload/ServletForUpload"; public static final int UPLOAD_SUCCESS=0x123; public static final int UPLOAD_FAIL=0x124; private Handler handler; public UploadService(Handler handler) { // TODO Auto-generated constructor stub this.handler=handler; } /** * @param params 請求參數,包括請求的的方法參數method如:“upload”, * 請求上傳的文件類型fileTypes如:“.jpg.png.docx” * @param files 要上傳的文件集合 */ public void uploadFileToServer(final Map<String, String> params, final ArrayList<File>files) { // TODO Auto-generated method stub new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub try { if (uploadFiles(url,params,files)) { handler.sendEmptyMessage(UPLOAD_SUCCESS);//通知主線程數據發送成功 }else { //將數據發送給服務器失敗 } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }).start(); } /** * @param url servlet的地址 * @param params 要傳遞的參數 * @param files 要上傳的文件 * @return true if upload success else false * @throws ClientProtocolException * @throws IOException */ private boolean uploadFiles(String url,Map<String, String>params,ArrayList<File>files) throws ClientProtocolException, IOException { HttpClient client=new DefaultHttpClient();// 開啟一個客戶端 HTTP 請求 HttpPost post = new HttpPost(url);//創建 HTTP POST 請求 MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setCharset(Charset.forName("uft-8"));//設置請求的編碼格式 builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);//設置瀏覽器兼容模式 int count=0; for (File file:files) { builder.addBinaryBody("file"+count, file); count++; } builder.addTextBody("method", params.get("method"));//設置請求參數 builder.addTextBody("fileTypes", params.get("fileTypes"));//設置請求參數 HttpEntity entity = builder.build();// 生成 HTTP POST 實體 post.setEntity(entity);//設置請求參數 HttpResponse response = client.execute(post);// 發起請求 並返回請求的響應 if (response.getStatusLine().getStatusCode()==200) { return true; } return false; } }