HttpClient使用MultipartEntityBuilder實現多文件上傳


一、MultipartEntityBuilder 實現文件上傳步驟  

 在HttpCient4.3之后上傳文件主要使用的類是位於org.apache.http.entity.mime下的MultipartEntityBuilder(原先的MultipartEntity已經基本棄用了)基本實現步驟如下: 
  1.設置上傳的模式;
  setMode(HttpMultipartMode mode),其中mode主要有BROWSER_COMPATIBLE,RFC6532,STRICT三種,默認值是STRICT。
  2.創建MultipartEntityBuilder對象,並添加需要上傳的數據;
  a.利用MultipartEntityBuilder.create()來創建對象;
  b.addBinaryBody:以二進制的形式添加數據,可以添加File、InputStream、byte[]類型的數據。
  addBinaryBody(String name, File file, ContentType contentType, String filename)
  addBinaryBody(String name, InputStream stream, ContentType contentType, String filename)
  addBinaryBody(String name, byte[] b, ContentType contentType, String filename)
  c.addTextBody:添加文本數據
  addTextBody(String name, String text, ContentType contentType)
  d.addPart:以Key/Value的形式添加ContentBody類型的數據
  addPart(String name, ContentBody contentBody)
   測試中選用了addBinaryBody。其中,第一個參數name的值,是服務器已經定義好的,服務器會根據這個字段來讀取我們上傳的文件流,不匹配則會報錯。關於contentType,可以參考:http://tool.oschina.net/commons,不同文件擴展名所對應的類型。而file/stream和fileName,就是我們所要上傳文件的信息。當用 瀏覽器的開發者工具查看API請求時,我們會看到這些/參數基本位於API請求中的 Request Payload字段。 
  3.利用build()方法創建一個HttpEntity對象;
  4.HttpEntity對象添加到指定的URL上,采用HttpPost的setEntity的方法;
  5.最后調用HttpClient對象發送請求,並獲取服務器的響應。 
 

 

 在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.jarhttpmime-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;        
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM