遇到一種業務場景,前端上傳的文件需要經過java服務轉發至文件服務。期間遇到了原生HttpClient怎麼使用的問題、怎麼把MultipartFile怎麼重新組裝成Http請求發送出去的問題、文件中文名亂碼問題。最後都解決了,先上代碼,再講遇到的坑 特別說明及遇到的坑: 1. 這裡基於tomcat進 ...
遇到一種業務場景,前端上傳的文件需要經過java服務轉發至文件服務。期間遇到了原生HttpClient怎麼使用的問題、怎麼把MultipartFile怎麼重新組裝成Http請求發送出去的問題、文件中文名亂碼問題。最後都解決了,先上代碼,再講遇到的坑
1 @Slf4j 2 @Service 3 public class FileServiceImpl implements IFileService { 4 5 @Value("${FileService.putUrl}") 6 private String putUrl; 7 @Value("${FileService.app_id}") 8 private String appId; 9 @Value("${FileService.securityKey}") 10 private String secureKey; 11 12 private final static String UPLOAD_RESPONSE_CODE = "error"; 13 private final static Integer UPLOAD_RESPONSE_SUCCESS = 0; 14 15 16 @Override 17 public String upload(MultipartFile file) { 18 19 int timeOut = 30000; 20 CloseableHttpClient httpClient = HttpClientBuilder.create().build(); 21 HttpHost proxy = new HttpHost("127.0.0.1", 62145, "http"); //設置本地fiddler代理,方便排查問題 22 RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeOut) 23 .setConnectTimeout(timeOut).setSocketTimeout(timeOut).setProxy(proxy).build(); 24 HttpPost httpPost = new HttpPost(putUrl); 25 httpPost.setConfig(requestConfig); 26 try { 27 //BROWSER_COMPATIBLE自定義charset,RFC6532=utf-8,STRICT=iso-8859-1 28 //此處一定要用RFC6532,網上普遍用的BROWSER_COMPATIBLE依然會出現中文名亂碼 29 MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532); 30 //multipartEntityBuilder.setCharset(Charset.forName("UTF-8")); //此處踩坑,轉發出去的filename依然為亂碼 31 //ContentType contentType = ContentType.create("multipart/form-data",Charset.forName("UTF-8")); //此處也是坑,轉發出去的filename依然為亂碼 32 multipartEntityBuilder.addBinaryBody("file", file.getInputStream(), ContentType.DEFAULT_BINARY, file.getOriginalFilename()); 33 34 multipartEntityBuilder.addTextBody("app_id", appId); //可替換成你自己需要的附加欄位 35 long time = System.currentTimeMillis() / 1000; 36 multipartEntityBuilder.addTextBody("time", String.valueOf(time)); //可替換成你自己需要的附加欄位 37 String beforeSign = String.format("app_id=%s&time=%s%s", appId, time, secureKey); 38 String sign = MD5Util.md5(beforeSign); 39 multipartEntityBuilder.addTextBody("sign", sign); //可替換成你自己需要的附加欄位 40 41 HttpEntity requestEntity = multipartEntityBuilder.build(); 42 httpPost.setEntity(requestEntity); 43 HttpResponse httpResponse = httpClient.execute(httpPost); 44 int statusCode= httpResponse.getStatusLine().getStatusCode(); 45 if (statusCode != 200) throw new BizException(BizCode.INNER_SERVICE_ERROR, "響應狀態碼為:" + statusCode); 46 HttpEntity responseEntity = httpResponse.getEntity(); 47 return getUrlString(EntityUtils.toString(responseEntity)); 48 } catch (Exception e) { 49 log.error("發送文件異常:{}", e); 50 throw new BizException(BizCode.INNER_SERVICE_ERROR, "發送文件服務異常:" + e.getMessage()); 51 } finally { 52 try { 53 httpClient.close(); 54 } catch (IOException e) { 55 log.error("關閉httpClient異常:" + e.getMessage(), e); 56 } 57 } 58 } 59 60 private String getUrlString(String jsonString) { 61 try{ 62 log.debug("解析json串:"+ jsonString); 63 JSONObject jsonObject = JSONObject.parseObject(jsonString); 64 if (jsonObject.getInteger(UPLOAD_RESPONSE_CODE) != UPLOAD_RESPONSE_SUCCESS) { 65 log.error("文件服務返回錯誤:" + jsonObject.getString("data")); 66 throw new OtherServiceReturnErrorException("文件服務返回錯誤:" + jsonObject.getString("data")); 67 } 68 return ((JSONObject)jsonObject.get("data")).get("original").toString(); 69 }catch (Exception e) { 70 log.error("文件服務返回json解析錯誤:" + jsonString); 71 throw new OtherServiceReturnErrorException("文件服務返回json解析錯誤:" + jsonString); 72 } 73 74 } 75 }
特別說明及遇到的坑:
1. 這裡基於tomcat進行請求轉發,需要在代碼中手動添加代理:
HttpHost proxy = new HttpHost("127.0.0.1", 62145, "http"); //設置本地fiddler代理,方便排查問題
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(timeOut)
.setConnectTimeout(timeOut).setSocketTimeout(timeOut).setProxy(proxy).build();
2. MultipartFile通過getInputStream()可以將流設置到MultipartEntityBuilder中,其中addBinaryBody裡面的ContentType 和 filename必須設置,要不然後續服務讀取不到這個文件流
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.RFC6532);
multipartEntityBuilder.addBinaryBody("file", file.getInputStream(), ContentType.DEFAULT_BINARY, file.getOriginalFilename());
3. file.getOriginalFilename()方法雖然沒有亂碼,但是addBinaryBody後,組裝的Http請求出去總是亂碼,如下圖:
踩了各種坑,如為MultipartEntityBuilder設置Charset或者是手動設置ContentType,都無法解決此問題,文件名依然是上圖所示亂碼
後來發現在MultipartEntityBuilder中設置Mode為HttpMultipartMode.RFC6532可以完美解決這個問題,並且不再需要單獨設置ContentType或Charset,因為HttpMultipartMode.RFC6532就告訴了MultipartEntityBuilder,裡面的數據都要使用UTF-8進行處理,fiddler抓到的請求發現filename成功變成中文名。
done