http使用post上传文件时,请求头和主体信息总结
請求頭必須配置如下行:
Content-Type' : 'multipart/form-data; boundary=---12321 ?boundary=---12321位文件的分界線
body如下:
"-----12321\r\n" ? ? ? ?//分割文件時加--
? ? ? ? ? ? ? ? "Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"\r\n"
? ? ? ? ? ? ? ? "Content-Type: text/plain\r\n\r\n" ? ??
? ? ? ? ? ? ? ? "inc-cd\n" test.txt的文件內容
? ? ? ? ? ? ? ? "-----12321\r\n"
假設接受文件的網(wǎng)頁程序位于?http://192.168.29.65/upload_file/UploadFile.假設我們要發(fā)送一個圖片文件,文件名為“kn.jpg”,
首先客戶端鏈接 192.168.24.56 后, 應該發(fā)送如下http 請求:
POST/logsys/home/uploadIspeedLog!doDefault.html?HTTP/1.1?
Accept: text/plain, */*?
Accept-Language: zh-cn?
Host: 192.168.24.56
Content-Type:multipart/form-data;boundary=-----------------------------7db372eb000e2
User-Agent: WinHttpClient?
Content-Length: 3693
Connection: Keep-Alive
-------------------------------7db372eb000e2
Content-Disposition: form-data; name="file"; filename="kn.jpg"
Content-Type: image/jpeg
(此處省略jpeg文件二進制數(shù)據(jù)...)
-------------------------------7db372eb000e2--
此內容必須一字不差,包括最后的回車,紅色字體部分就是協(xié)議的頭。給服務器上傳數(shù)據(jù)時,并非協(xié)議頭每個字段都得說明,其中,content-type是必須的,它包括一個類似標志性質的名為boundary的標志,它可以是隨便輸入的字符串。對后面的具體內容也是必須的。它用來分辨一段內容的開始。Content-Length: 3693 ,這里的3693是要上傳文件的總長度。綠色字體部分就是需要上傳的數(shù)據(jù),可以是文本,也可以是圖片等。數(shù)據(jù)內容前面需要有Content-Disposition, Content-Type以及Content-Transfer-Encoding等說明字段。最后的紫色部分就是協(xié)議的結尾了。
注意這一行:
Content-Type: multipart/form-data; boundary=---------------------------7db372eb000e2
根據(jù) rfc1867, multipart/form-data是必須的.?
---------------------------7db372eb000e2?是分隔符,分隔多個文件、表單項。其中b372eb000e2?是即時生成的一個數(shù)字,用以確保整個分隔符不會在文件或表單項的內容中出現(xiàn)。Form每個部分用分隔符分割,分隔符之前必須加上"--"著兩個字符(即--{boundary})才能被http協(xié)議認為是Form的分隔符,表示結束的話用在正確的分隔符后面添加"--"表示結束。
前面的 ---------------------------7d 是 IE 特有的標志,Mozila 為---------------------------71.?
?
?
每個分隔的數(shù)據(jù)的都可以用Content-Type來表示下面數(shù)據(jù)的類型,可以參考rfc1341 (http://www.ietf.org/rfc/rfc1341.txt)
例如?:Contect-Type:image/jpeg 表示下面的數(shù)據(jù)是jpeg文件數(shù)據(jù)
[轉]通過 http 協(xié)議上傳文件
2008-04-30 23:55
| 1、概述 在最初的 http 協(xié)議中,沒有上傳文件方面的功能。 rfc1867 (?http://www.ietf.org/rfc/rfc1867.txt?) 為 http 協(xié)議添加了這個功能。客戶端的瀏覽器,如 Microsoft IE, Mozila, Opera 等,按照此規(guī)范將用戶指定的文件發(fā)送到服務器。服務器端的網(wǎng)頁程序,如 php, asp, jsp 等,可以按照此規(guī)范,解析出用戶發(fā)送來的文件。 Microsoft IE, Mozila, Opera 已經(jīng)支持此協(xié)議,在網(wǎng)頁中使用一個特殊的 form 就可以發(fā)送文件。 絕大部分 http server ,包括 tomcat ,已經(jīng)支持此協(xié)議,可接受發(fā)送來的文件。 各種網(wǎng)頁程序,如 php, asp, jsp 中,對于上傳文件已經(jīng)做了很好的封裝。 2、上傳文件的實例:用 servelet 實現(xiàn)(http server 為 tomcat 4.1.24) 1. 在一個 html 網(wǎng)頁中,寫一個如下的form : <form?enctype="multipart/form-data"?action="http://192.168.29.65/UploadFile"?method=post> ??? load multi files :<br> ??? <input name="userfile1"?type="file"><br> ??? <input name="userfile2" type="file"><br> ??? <input name="userfile3" type="file"><br> ??? <input name="userfile4" type="file"><br> ??? text field :<input type="text" name="text" value="text"><br> ??? <input type="submit" value="提交"><input type=reset> </form> 2. 服務端 servelet 的編寫 現(xiàn)在第三方的 http upload file 工具庫很多。Jarkata 項目本身就提供了fileupload 包http://jakarta.apache.org/commons/fileupload/?。文件上傳、表單項處理、效率問題基本上都考慮到了。在 struts 中就使用了這個包,不過是用 struts 的方式另行封裝了一次。這里我們直接使用 fileupload 包。至于struts 中的用法,請參閱 struts 相關文檔。 這個處理文件上傳的 servelet 主要代碼如下: public void doPost( HttpServletRequest request, HttpServletResponse response ) { ??? DiskFileUpload diskFileUpload = new DiskFileUpload(); ??? //?允許文件最大長度 ??? diskFileUpload.setSizeMax( 100*1024*1024 ); ??? //?設置內存緩沖大小 ??? diskFileUpload.setSizeThreshold( 4096 ); ??? //?設置臨時目錄 ??? diskFileUpload.setRepositoryPath( "c:/tmp" ); ??? List fileItems = diskFileUpload.parseRequest( request ); ??? Iterator iter = fileItems.iterator(); ??? for( ; iter.hasNext(); ) { ??????? FileItem fileItem = (FileItem) iter.next(); ??????? if( fileItem.isFormField() ) { ??????????? //?當前是一個表單項 ??????????? out.println( "form field : " + fileItem.getFieldName() + ", " + fileItem.getString() ); ??????? } else { ??????????? //?當前是一個上傳的文件 ??????????? String fileName = fileItem.getName(); ??????????? fileItem.write( new File("c:/uploads/"+fileName) ); ??????? } ??? } } 為簡略起見,異常處理,文件重命名等細節(jié)沒有寫出。 3、 客戶端發(fā)送內容構造 假設接受文件的網(wǎng)頁程序位于?http://192.168.29.65/upload_file/UploadFile. 假設我們要發(fā)送一個二進制文件、一個文本框表單項、一個密碼框表單項。文件名為 E:\s ,其內容如下:(其中的XXX代表二進制數(shù)據(jù),如 01 02 03) a bb XXX ccc 客戶端應該向 192.168.29.65 發(fā)送如下內容: POST /upload_file/UploadFile HTTP/1.1 Accept: text/plain, */* Accept-Language: zh-cn Host: 192.168.29.65:80 Content-Type:multipart/form-data;boundary=---------------------------7d33a816d302b6 User-Agent: Mozilla/4.0 (compatible; OpenOffice.org) Content-Length: 424 Connection: Keep-Alive -----------------------------7d33a816d302b6 Content-Disposition: form-data; name="userfile1";?filename="E:\s" Content-Type: application/octet-stream a bb XXX ccc -----------------------------7d33a816d302b6 Content-Disposition: form-data; name="text1" foo -----------------------------7d33a816d302b6 Content-Disposition: form-data; name="password1" bar -----------------------------7d33a816d302b6-- 此內容必須一字不差,包括最后的回車。 注意:Content-Length: 424 這里的424是紅色內容的總長度(包括最后的回車) 注意這一行: Content-Type: multipart/form-data; boundary=---------------------------7d33a816d302b6 根據(jù) rfc1867, multipart/form-data是必須的. ---------------------------7d33a816d302b6 是分隔符,分隔多個文件、表單項。其中33a816d302b6 是即時生成的一個數(shù)字,用以確保整個分隔符不會在文件或表單項的內容中出現(xiàn)。前面的 ---------------------------7d 是 IE 特有的標志。 Mozila 為---------------------------71 用手工發(fā)送這個例子,在上述的 servlet 中檢驗通過。 (上面有一個回車)用戶可以選擇多個文件,填寫表單其它項,點擊“提交”按鈕后就開始上傳給http://192.168.29.65/upload_file/UploadFile?這是一個 servelet 程序 注意 enctype="multipart/form-data", method=post, type="file" 。根據(jù) rfc1867, 這三個屬性是必須的。multipart/form-data 是新增的編碼類型,以提高二進制文件的傳輸效率。具體的解釋請參閱 rfc1867 // HEADER: ? 寫道 ......Content-Type: multipart/form-data; ? ? BODY: ? Content-type: multipart/form-data, boundary=AaB03x--AaB03xcontent-disposition: form-data; name="field1"Joe Blow--AaB03xcontent-disposition: form-data; name="pics"Content-type: multipart/mixed, boundary=BbC04y--BbC04yContent-disposition: attachment; filename="file1.txt"? ? 注釋: 1 傳文件的時候 使用的Media Type name: multipart , Media subtype name: form-data 2 boundary用來標識分割不同的field,其中文件是一個特殊的field 3 多個文件的時候得繼續(xù)制定?Content-type: multipart/mixed ,同時定義新的 boundary 4 不同的field注意區(qū)分?Content-disposition 的值,是 form/data 還是 attachment 5 這里定義的是POST方式的上傳,不針對PUT 。 ? ? 2 如果需求非常確定每次請求只上傳一個文件,那么我們可以不使用HTTP約定的這種方式,而改用一種更簡單直接的方式: ? - 在 HEDER 里的 POST 后的URL里攜帶普通參數(shù) field - 在 BODY里直接裝在要上傳的文件內容,拋棄任何格式等約束 - 服務端直接從BODY里讀取流數(shù)據(jù)保存為文件,其他參數(shù)從URL里讀取 ? 這樣以來HTTP報文就類似: ? HEDER ? 寫道 POST /HttpFileServer/upload?filename=nodexy.zip&fid=t01 HTTP/1.1Host: www.yangzt.com:9190 Content-Length: xxxxx ? BODY ? 寫道 文件內容? 注釋: 1 這不是標準的文件上傳方式,但仍然是標準的HTTP報文 2 這種私有約定的方式,需要服務端和客戶端同時特異化處理 3 針對每次請求只傳一個文件的需求,這樣改良后就會比較簡潔,至于效率上是否有明顯差別還不得而知,未做測試對比 ? ? 3 總結: ? 在HTTP協(xié)議這一層上做文件的上傳下載,也是很常見的方式,尤其很多移動應用里會采用;因為對客戶端來說打開一個URL來GET或POST數(shù)據(jù),相比打開一個scocke連接來讀取或寫入數(shù)據(jù)要簡單得多,實現(xiàn)也快捷高效。 ? HTTP協(xié)議上的上傳下載,也可以輕松實現(xiàn)斷點續(xù)傳,和進度反饋等,主要依賴length和range兩個值。所以作為標準考慮,一定要明確地設置header里的length屬性 --- 如果不設置,對于一般瀏覽器來說無礙,仍然可以成功下載,但是對于諸如libcurl這樣的類庫來說則無法取到數(shù)據(jù)。 ? 另外,TCP協(xié)議層上的文件上傳下載,也是很常見的應用場景,改日再次涉及另作詳談。 /
在開發(fā)中,我們使用的比較多的HTTP請求方式基本上就是GET、POST。其中GET用于從服務器獲取數(shù)據(jù),POST主要用于向服務器提交一些表單數(shù)據(jù),例如文件上傳等。而我們在使用HTTP請求時中遇到的比較麻煩的事情就是構造文件上傳的HTTP報文格式,這個格式雖說也比較簡單,但也比較容易出錯。今天我們就一起來學習HTTP POST的報文格式以及通過Java來模擬文件上傳的請求。 首先我們來看一個POST的報文請求,然后我們再來詳細的分析它。 POST報文格式POST /api/feed/ HTTP/1.1 Accept-Encoding: gzip Content-Length: 225873 Content-Type: multipart/form-data; boundary=OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp Host: www.myhost.com Connection: Keep-Alive--OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp Content-Disposition: form-data; name="lng" Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit116.361545 --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp Content-Disposition: form-data; name="lat" Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit39.979006 --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp Content-Disposition: form-data; name="images"; filename="/storage/emulated/0/Camera/jdimage/1xh0e3yyfmpr2e35tdowbavrx.jpg" Content-Type: application/octet-stream Content-Transfer-Encoding: binary這里是圖片的二進制數(shù)據(jù) --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp--這里我們提交的是經(jīng)度、緯度和一張圖片(圖片數(shù)據(jù)比較長,而且比較雜亂,這里省略掉了)。 格式分析請求頭分析我們先看?報文格式中的第一行: POST /api/feed/ HTTP/1.1 這一行就說明了這個請求的請求方式,即為POST方式,要請求的子路徑為/api/feed/,例如我們的服務器地址為www.myhost.com,然后我們的這個請求的完整路徑就是?www.myhost.com/api/feed/,最后說明了HTTP協(xié)議的版本號為1.1。 Accept-Encoding: gzip Content-Length: 225873 Content-Type: multipart/form-data; boundary=OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp Host: www.myhost.com Connection: Keep-Alive 這幾個header的意思分別為服務器返回的數(shù)據(jù)需要使用gzip壓縮、請求的內容長度為225873、內容的類型為"multipart/form-data"、請求參數(shù)分隔符(boundary)為OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp、請求的根域名為www.myhost.com、HTTP連接方式為持久連接( Keep-Alive)。其中這里需要注意的一點是分隔符,即boundary。?boundary用于作為請求參數(shù)之間的界限標識,例如參數(shù)1和參數(shù)2之間需要有一個明確的界限,這樣服務器才能正確的解析到參數(shù)1和參數(shù)2。但是分隔符并不僅僅是boundary,而是下面這樣的格式:-- + boundary。例如這里的boundary為?OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp,那么參數(shù)分隔符則為: --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp 不管boundary本身有沒有這個"--",這個"--"都是不能省略的。我們知道HTTP協(xié)議采用“請求-應答”模式,當使用普通模式,即非KeepAlive模式時,每個請求/應答客戶和服務器都要新建一個連接,完成之后立即斷開連接(HTTP協(xié)議為無連接的協(xié)議);當使用Keep-Alive模式(又稱持久連接、連接重用)時,Keep-Alive功能使客戶端到服務器端的連接持續(xù)有效,當出現(xiàn)對服務器的后續(xù)請求時,Keep-Alive功能避免了建立或者重新建立連接。
如上圖中,左邊的是關閉Keep-Alive的情況,每次請求都需要建立連接,然后關閉連接;右邊的則是Keep-Alive,在第一次建立請求之后保持連接,然后后續(xù)的就不需要每次都建立、關閉連接了,?啟用Keep-Alive模式肯定更高效,性能更高,因為避免了建立/釋放連接的開銷?。 http 1.0中默認是關閉的,需要在http頭加入"Connection: Keep-Alive",才能啟用Keep-Alive;http 1.1中默認啟用Keep-Alive,如果加入"Connection: close ",才關閉。目前大部分瀏覽器都是用http1.1協(xié)議,也就是說默認都會發(fā)起Keep-Alive的連接請求了,所以是否能完成一個完整的Keep- Alive連接就看服務器設置情況。 請求實體分析請求實體其實就是HTTP POST請求的參數(shù)列表,每個參數(shù)以請求分隔符開始,即-- + boundary。例如下面這個參數(shù)。 --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp Content-Disposition: form-data; name="lng" Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit116.361545 上面第一行為--OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp,也就是--加上boundary內容,?最后加上一個換行 (這個換行不能省略),換行的字符串表示為"\r\n"?。第二行為Content-Disposition和參數(shù)名,這里的參數(shù)名為lng,即經(jīng)度。?Content-Disposition就是當用戶想把請求所得的內容存為一個文件的時候提供一個默認的文件名,這里我們不過多關注。第三行為?Content-Type,即?WEB 服務器告訴瀏覽器自己響應的對象的類型?,還有指定字符編碼為UTF-8。?第四行是?描述的是消息請求(request)和響應(response)所附帶的實體對象(entity)的傳輸形式,?簡單文本數(shù)據(jù)我們設置為8bit,文件參數(shù)我們設置為binary就行?。然后添加兩個換行之后才是參數(shù)的具體內容。例如這里的參數(shù)內容為116.361545。注意這里的每行之間都是使用“\r\n”來換行的,最后一行和參數(shù)內容之間是兩個換行。文件參數(shù)也是一樣的格式,只是文件參數(shù)的內容是字節(jié)流。 這里要注意一下,普通文本參數(shù)和文件參數(shù)有如下兩個地方的不同,因為其內容本身的格式是不一樣的。 普通參數(shù): Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit文件參數(shù): Content-Type: application/octet-stream Content-Transfer-Encoding: binary參數(shù)實體的最后一行是: --加上boundary加上--,最后換行,這里的 格式即為: --OCqxMF6-JxtxoMDHmoG5W5eY9MGRsTBp--。 模擬文件上傳請求public static void uploadFile(String fileName) {try { // 換行符 final String newLine = "\r\n"; final String boundaryPrefix = "--"; // 定義數(shù)據(jù)分隔線 String BOUNDARY = "========7d4a6d158c9"; // 服務器的域名 URL url = new URL("www.myhost.com"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 設置為POST情 conn.setRequestMethod("POST"); // 發(fā)送POST請求必須設置如下兩行 conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); // 設置請求頭參數(shù) conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("Charsert", "UTF-8"); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); OutputStream out = new DataOutputStream(conn.getOutputStream()); // 上傳文件 File file = new File(fileName); StringBuilder sb = new StringBuilder(); sb.append(boundaryPrefix); sb.append(BOUNDARY); sb.append(newLine); // 文件參數(shù),photo參數(shù)名可以隨意修改 sb.append("Content-Disposition: form-data;name=\"photo\";filename=\"" + fileName + "\"" + newLine); sb.append("Content-Type:application/octet-stream"); // 參數(shù)頭設置完以后需要兩個換行,然后才是參數(shù)內容 sb.append(newLine); sb.append(newLine); // 將參數(shù)頭的數(shù)據(jù)寫入到輸出流中 out.write(sb.toString().getBytes()); // 數(shù)據(jù)輸入流,用于讀取文件數(shù)據(jù) DataInputStream in = new DataInputStream(new FileInputStream( file)); byte[] bufferOut = new byte[1024]; int bytes = 0; // 每次讀1KB數(shù)據(jù),并且將文件數(shù)據(jù)寫入到輸出流中 while ((bytes = in.read(bufferOut)) != -1) { out.write(bufferOut, 0, bytes); } // 最后添加換行 out.write(newLine.getBytes()); in.close(); // 定義最后數(shù)據(jù)分隔線,即--加上BOUNDARY再加上--。 byte[] end_data = (newLine + boundaryPrefix + BOUNDARY + boundaryPrefix + newLine) .getBytes(); // 寫上結尾標識 out.write(end_data); out.flush(); out.close(); // 定義BufferedReader輸入流來讀取URL的響應 // BufferedReader reader = new BufferedReader(new InputStreamReader( // conn.getInputStream())); // String line = null; // while ((line = reader.readLine()) != null) { // System.out.println(line); // }} catch (Exception e) { System.out.println("發(fā)送POST請求出現(xiàn)異常!" + e); e.printStackTrace();}}使用Apache Httpmime上傳文件/*** @param fileName 圖片路徑*/public static void uploadFileWithHttpMime(String fileName) {// 定義請求urlString uri = "www.myhost.com";// 實例化http客戶端HttpClient httpClient = new DefaultHttpClient();// 實例化post提交方式HttpPost post = new HttpPost(uri);// 添加json參數(shù)try { // 實例化參數(shù)對象 MultipartEntity params = new MultipartEntity(); // 圖片文本參數(shù) params.addPart("textParams", new StringBody( "{'user_name':'我的用戶名','channel_name':'卻道明','channel_address':'(123.4,30.6)'}", Charset.forName("UTF-8"))); // 設置上傳文件 File file = new File(fileName); // 文件參數(shù)內容 FileBody fileBody = new FileBody(file); // 添加文件參數(shù) params.addPart("photo", fileBody); params.addPart("photoName", new StringBody(file.getName())); // 將參數(shù)加入post請求體中 post.setEntity(params); // 執(zhí)行post請求并得到返回對象 [ 到這一步我們的請求就開始了 ] HttpResponse resp = httpClient.execute(post); // 解析返回請求結果 HttpEntity entity = resp.getEntity(); InputStream is = entity.getContent(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuffer buffer = new StringBuffer(); String temp; while ((temp = reader.readLine()) != null) { buffer.append(temp); } System.out.println(buffer);} catch (UnsupportedEncodingException e) { e.printStackTrace();} catch (ClientProtocolException e) { e.printStackTrace();} catch (IOException e) { e.printStackTrace();} catch (IllegalStateException e) { e.printStackTrace();}} |
總結
以上是生活随笔為你收集整理的http使用post上传文件时,请求头和主体信息总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lol炼金法强多少合适
- 下一篇: 不孕可以人工授精吗