formdata上传文件_关于multipart/formdata上传文件
最近在做一個文件上傳的開放接口,用到Content-Type: multipart/form-data這種請求類型,特地做了一些研究和記錄。
在最初的 http協議中,并沒有上傳文件方面的功能。RFC1867為 http協議添加了這個能力。常見的瀏覽器,如 Microsoft IE, Mozila, Opera, Chrome,Safari等都已經支持。按照此規范將用戶指定的文件發送到服務器。服務器端的程序如 java等,可以按照此規范解析出用戶發送來的文件。
RFC1867定義的請求格式如下示例:
------ZcyOpenBoundaryEEpIo3GVWKVCPrX8Content-Disposition: form-data; name="_data_"Content-Type: text/plain; charset=UTF-8Content-Transfer-Encoding: 8bit{"fileName":"redis-use.png","bizCode":"1071"}------ZcyOpenBoundaryEEpIo3GVWKVCPrX8Content-Disposition: form-data; name="file"; filename="deaultFilename"Content-Type: application/octet-streamContent-Transfer-Encoding: binary???àJFIF??C $.' ",#(7),01444'9=82<.>342??C ?à22"????3!1Aa"Qq?á2BR#3¢±???? !13QA?ú???{?Z3??j?êμr??c?÷·μO ??K?¥Tv?êhíI§~2,oFT4é÷?ò???1¢Y?9 etúíD=T?a?1Mo :?~t?StY)vè°l|t??eaHù?=E>?¤R3μ3/?E?Tb??í §\6£O-J#4Y÷?Fà?h_E5aw¢§?g?÷1Vˉ/-?·?3nDT=9?òai ,x?S^2?x|êF2?D?H?ò?±}K;h×Zó????YDàx?Z4]?|àr_?-y??q4ó2FV?àá??ì\óˉá×%?6[=P?9l??cJc?;-2??'òPYè?5é?=′?%?·üFc?1%±?'SL?′í?8¢?y@zc^]?Tm8·Ss"é1?QwG?'t?á¢êQ?YμmáDyimcl?4§H35??l?p4=¥,·(íA~x?O~]êjWòa?ók-g??%?la&27?r[áL¤?ìr7|II?14?`Q^í,$¤gw9?(??ùY?StT??ìˉ¨]?WT??%bˉC]EVê?ot?:ùê|?,¨>?ìw¢?0K7£?.?:Tò${B0a 1E¢?U?éé??EQ ?ù...省略部分內容...------ZcyOpenBoundaryEEpIo3GVWKVCPrX8--這里的"----ZcyOpenBoundaryEEpIo3GVWKVCPrX8"是規范中定義的boundary。http傳輸的內容通過boundary進行了分割,以--${boundary}開始,并以${boundary}--結尾。
明白了以上內容,我們再來看如何使用multipart/form-data進行文件上傳。以HttpClient為例進行說明,其他工具大同小異。首先想到的就是要配置 http請求頭信息中的Content-Type字段,沒錯,我們來看如何進行設置:
httpPost.addHeader("Content-Type", "multipart/form-data; boundary=----ZcyOpenBoundaryEEpIo3GVWKVCPrX8");注意,這里multipart/form-data 后面要跟上boundary。當然,我們也可以不進行Content-Type設置,一般工具都會為我們自動生成規范的Content-Type,自動生成過程不在本次討論范圍內,讀者可以自行閱讀代碼。
繼續,我們設置了請求頭中的boundary以后還要確保與代碼片段1中的boundary保持一致,否則服務端無法讀取到請求體信息。服務端正常情況下收到的請求是下面的樣子:
當然,上圖是以Spring框架為例,其他框架或語言亦大同小異。
那么怎么保證請求頭中的boundary與代碼片段1中的boundary一致呢?一種辦法是模擬http請求手寫拼接報文:
String BOUNDARY = "----ZcyOpenBoundaryEEpIo3GVWKVCPrX8";StringBuffer?sb?=?new?StringBuffer();// 發送字段for(int?i=0;?i sb = sb.append("--"); sb = sb.append(BOUNDARY); sb = sb.append("\r\n"); sb = sb.append("Content-Disposition: form-data; name=\""+ props[i] + "\"\r\n\r\n"); sb = sb.append(URLEncoder.encode(values[i])); sb = sb.append("\r\n");}// 發送文件:sb = sb.append("--");sb = sb.append(BOUNDARY);sb = sb.append("\r\n");sb = sb.append("Content-Disposition: form-data; name=\"1\"; filename=\"1.txt\"\r\n");sb = sb.append("Content-Type: application/octet-stream\r\n\r\n");byte[] data = sb.toString().getBytes();byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();// 設置HTTP頭hc.setRequestProperty("Content-Type", MULTIPART_FORM_DATA + "; boundary=" + BOUNDARY);hc.setRequestProperty("Content-Length", String.valueOf(data.length + file.length + end_data.length));// 輸出output = client.openOutputStream();output.write(data);output.write(file);output.write(end_data);......當然以上方式比較原始,容易出錯,我們更喜歡用高級語言。下面還是以HttpClient為例:
String?result?=?"";String?boundary?="----ZcyOpenBoundaryEEpIo3GVWKVCPrX8";try?(CloseableHttpClient httpClient = HttpClients.createDefault()){ String fileName = file.getName(); HttpPost httpPost = new HttpPost(url); //設置請求頭????httpPost.setHeader("Content-Type","multipart/form-data;?boundary="+boundary); MultipartEntity multipartEntity = new MultipartEntity(HttpMultipartMode.STRICT, boundary, Charset.defaultCharset()); ????...省略內容... httpPost.setEntity(multipartEntity); // 執行提交 HttpResponse response = httpClient.execute(httpPost); if (response.getStatusLine().getStatusCode() == 200) { //響應 HttpEntity responseEntity = response.getEntity();??????if?(responseEntity?!=?null)?{ // 將響應內容轉換為字符串 result = EntityUtils.toString(responseEntity, Charset.forName("UTF-8")); } } } catch (IOException e) { e.printStackTrace();} catch (Exception e) { e.printStackTrace();}?System.out.println("result="?+ result);注意,上述代碼中除了設置header頭中的boundary外,還要同時設置MultipartEntity對象中的boundary,這樣就保持一致啦。
至此,服務端已經可以獲取到期待已久的文件流信息了。
總結
以上是生活随笔為你收集整理的formdata上传文件_关于multipart/formdata上传文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【LeetCode笔记】剑指 Offer
- 下一篇: python语言浮点数可以不带小数部分吗