HttpClient多檔案上傳程式碼及普通引數中文亂碼問題解決

為何不是夢發表於2019-07-20

該隨筆記錄了在實際專案中使用HttpClient呼叫外部api,需上傳檔案和普通引數的程式碼。

筆者在使用 HttpClient 呼叫 http api 介面時,需要服務端上傳檔案和一些普通引數給 http api,如果使用 Java 自帶的 HttpURLConnection 請求的話,傳送 multipart/form-data + POST 請求會比較麻煩,需要設定一些邊界(將檔案與檔案、檔案與普通引數之間隔開,便於接收者擷取,這是 http 協議要求的)。

因為上傳檔案和普通引數時,服務端讀取報文是根據邊界值來擷取的,如果使用原生的 HttpURLConnection 則比較麻煩,所以筆者採用 HttpClient 工具,httpclient是apache 軟體基金會下的子專案,它很好的封裝了Http工具,物件導向的思想省去了很多細節,使程式設計師關注與業務邏輯處理,不用關注這些通訊細節。

筆者使用HTTPClient實現檔案的上傳,使用 MultipartEntityBuilder 構造請求體,實現 multipart/form-data + POST 請求http介面。下面提供了使用時的程式碼實現,包括服務端和客戶端。

不過,筆者在使用的過程中發現,當傳遞的普通引數有中文時,對方接到的引數會亂碼,因為開始筆者使用的是multipartEntity.addTextBody(key, postParam.get(key));的方式設定普通引數。

為了解決亂碼問題,最後查到了解決辦法,記錄如下。

如下程式碼是可以上傳多個檔案和普通引數的,使用 multipart/form-data + POST 方式提交,模擬瀏覽器在頁面上 form表單 的提交方式。


 

客戶端上傳檔案及普通引數程式碼:

 1 /**
 2      * httpclient 檔案上傳
 3      * @param postFiles
 4      * @param postUrl
 5      * @param postParam
 6      * @return
 7      */
 8     public static Map<String, Object> uploadFileByHttpPost(File[] postFiles, String postUrl, Map<String, String> postParam) {
 9         Map<String, Object> resultMap = new HashMap<String, Object>();
10         CloseableHttpClient httpClient = HttpClients.createDefault();
11         try {
12             //把一個普通引數和檔案上傳給下面這個api介面
13             HttpPost httpPost = new HttpPost(postUrl);
14             //設定傳輸引數,設定編碼。設定瀏覽器相容模式,解決檔名亂碼問題
15             MultipartEntityBuilder multipartEntity = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
16             for (int i = 0; i < postFiles.length; i++) {
17                 File postFile = postFiles[i];
18                 FileBody fundFileBin = new FileBody(postFile, ContentType.MULTIPART_FORM_DATA);
19 
20                 //相當於<input type="file" name="media"/>
21                 multipartEntity.addPart("upload_file"+i, fundFileBin);
22             }
23             //把檔案轉換成流物件FileBody
24             Set<String> keySet = postParam.keySet();
25             for (String key : keySet) {
26                 //解決中文亂碼
27                 ContentType contentType = ContentType.create("text/plain", Charset.forName("UTF-8"));
28                 StringBody stringBody = new StringBody(postParam.get(key), contentType);
29                 multipartEntity.addPart(key, stringBody);
30                 // multipartEntity.addTextBody(key, postParam.get(key));//這個中文會亂碼
31             }
32             HttpEntity reqEntity = multipartEntity.build();
33             httpPost.setEntity(reqEntity);
34             //發起請求 並返回請求的響應
35             CloseableHttpResponse response = httpClient.execute(httpPost);
36             try {
37                 //列印響應狀態
38                 resultMap.put("statusCode", response.getStatusLine().getStatusCode());
39                 //獲取響應物件
40                 HttpEntity resEntity = response.getEntity();
41                 if (resEntity != null) {
42                     //列印響應內容
43                     resultMap.put("data", EntityUtils.toString(resEntity, Charset.forName("UTF-8")));
44                 }
45                 //銷燬
46                 EntityUtils.consume(resEntity);
47             } catch (Exception e) {
48                 e.printStackTrace();
49             } finally {
50                 response.close();
51             }
52         } catch (ClientProtocolException e1) {
53             e1.printStackTrace();
54         } catch (IOException e1) {
55             e1.printStackTrace();
56         } finally {
57             try {
58                 httpClient.close();
59             } catch (IOException e) {
60                 e.printStackTrace();
61             }
62         }
63         return resultMap;
64     }

 


 服務端處理請求程式碼

這是客戶端上傳的程式碼,我們看一下怎麼接收,使用SpringMVC controller層接收檔案和普通引數:

 

 1 /**
 2      * 上傳檔案
 3      * @throws IOException
 4      * @throws IllegalStateException
 5      */
 6     @RequestMapping("/postFile")
 7     @ResponseBody
 8     public String postFile(HttpServletRequest request){
 9         Map<String, Object> map = new HashMap<String, Object>();
10         // 建立一個通用的多部分解析器
11         CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(request
12                 .getSession().getServletContext());
13         String name = request.getParameter("name");
14         String age = request.getParameter("age");
15 
16         System.out.println(name+","+age);
17         request.getSession().getServletContext();
18         // 判斷 request 是否有檔案上傳,即多部分請求
19         if (multipartResolver.isMultipart(request)) {
20             // 轉換成多部分request
21             MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
22             // 取得request中的所有檔名
23             Iterator<String> iter = multiRequest.getFileNames();
24             while (iter.hasNext()) {//多檔案
25                 // 取得上傳檔案
26                 MultipartFile multipartFile = multiRequest.getFile(iter.next());
27                 if (null != multipartFile) {
28                     // 取得當前上傳檔案的檔名稱
29                     String fileName = multipartFile.getOriginalFilename();
30                     if (fileName.trim() != null && fileName.trim().length() > 0) {
31                         CommonsMultipartFile cf = (CommonsMultipartFile) multipartFile;
32                         DiskFileItem fi = (DiskFileItem) cf.getFileItem();
33                         File tempFile = fi.getStoreLocation();
34                         // 拿到檔案,儲存
35                         try {
36                             multipartFile.transferTo(new File("F:\\static\\page\\"+multipartFile.getOriginalFilename()));
37                         } catch (IOException e) {
38                             e.printStackTrace();
39                             return "error";
40                         }
41                     }
42                 }
43             }
44         }
45         return "success";
46     }

 


 

 測試程式碼:

 1 public static void main(String[] args) {
 2         String url = "http://localhost:8080/postFile";
 3         File[] files = new File[2];
 4         files[0] = new File("F:\\static\\updateFile-demo.docx");
 5         files[1] = new File("F:\\static\\updateFile-demo02.docx");
 6 
 7         Map<String,String> param = new HashMap<>();
 8         param.put("name","程式設計大道");
 9         param.put("age","18");
10 
11         Map<String, Object> stringObjectMap = HttpClientUtil01.uploadFileByHttpPost(files, url, param);
12         System.out.println(stringObjectMap);
13     }

 


 

程式碼驗證

我們上傳兩個檔案和兩個普通引數,服務端controller裡的處理是,列印這兩個普通引數並把兩個檔案儲存到page目錄下

 

 啟動服務

 我們先啟動服務端,如下圖正常啟動

 執行測試類,控制檯輸出如下:

服務端輸出:

檢視是否儲存成功

成功!!

 


 

注意事項:

但是在成功之前也遇到了問題,如下:

上傳檔案:

 


普通引數,中文亂碼:

 

相關文章