構建multipart/form-data實現文件上傳
通常文件上傳都是通過
form
表單中的file
控件,並將form中的content-type設置爲multipart/form-data
。現在我們通過java來構建這部分請求內容實現文件上傳功能。
一、關於multipart/form-data
文件上傳本質上是一個POST
請求。只不過請求頭以及請求內容遵循一定的規則(協議)
-
請求頭(Request Headers)中需要設置
Content-Type
爲multipart/form-data; boundary=${boundary}
。其中${boundary}
分割線,需要在代碼中替換,且儘量複雜,不易重複 -
請求正文(Request Body)需要使用在 Header中設置的
${boundary}
來分割當前正文中的FormItem,內容格式如下--${boundary} Content-Disposition: form-data; name="id" testCodeUpload --${boundary} Content-Disposition: form-data; name="file";filename="xx.txt" Content-Type: application/octet-stream {{這裏寫入文件流}} --${boundary}--
正文開始以
前綴
+${boundary}開始,以前綴
+${boundary}+前綴
結束。中間每個FormItem
以前綴
+${boundary}開始,以一個空白的換行結束。
二、代碼實現
實例代碼採用
HttpURLConnection
實現一個簡單POST
請求
-
建立
http
請求,設置基本參數
URL postUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) postUrl.openConnection(); conn.setRequestMethod("POST"); conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("Charset", "UTF-8");
-
添加文件上傳必須的請求信息,獲取http請輸出流
String boundary = "----" + UUID.randomUUID().toString(); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); OutputStream out = conn.getOutputStream(); StringBuilder sb = new StringBuilder();
-
一組
FormItem
sb.append(boundaryPrefix); sb.append(boundary); sb.append(newLine); sb.append("Content-Disposition: form-data; name=\"id\""); sb.append(newLine); sb.append(newLine); sb.append("testCodeUpload"); sb.append(newLine);
-
文件寫人
sb.append(boundaryPrefix); sb.append(boundary); sb.append(newLine); sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"" + fileName + "\""); sb.append("Content-Type: application/octet-stream"); sb.append(newLine); sb.append(newLine); out.write(sb.toString().getBytes()); File file = new File(file1); FileInputStream in = new FileInputStream(file); byte[] bufferOut = new byte[1024]; int bytes = 0; while ((bytes = in.read(bufferOut)) != -1) { out.write(bufferOut, 0, bytes); } out.write(newLine.getBytes()); in.close();
- 結束標誌 前綴+boundary +前綴
byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine) .getBytes(); out.write(end_data); out.flush(); out.close();
三、文件接收
-
文件接收端通過迭代每個FileItem獲取不同的數據
FileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); upload.setHeaderEncoding("UTF-8"); try { items = upload.parseRequest(request); } catch (FileUploadException ex) { ex.printStackTrace(); out.println(ex.getMessage()); return; } Iterator<FileItem> itr = items.iterator(); String id = "", fileName = ""; int chunks = 1, chunk = 0; FileItem tempFileItem = null; while (itr.hasNext()) { FileItem item = (FileItem) itr.next(); if (item.getFieldName().equals("id")) { id = item.getString(); } else if (item.getFieldName().equals("name")) { fileName = new String(item.getString().getBytes("ISO-8859-1"), "UTF-8"); } else if (item.getFieldName().equals("file")) { tempFileItem = item; }
四、總結
通過代碼實現一遍文件上傳,瞭解其運行機制,解開了以前在寫文件上傳代碼中item.getFieldName().equals("name")
等相關判斷的疑惑。所以,對於已有的基礎代碼,還是多看,多寫,多實踐。
附完整代碼
1 @Test 2 public void buildUploadStream() throws IOException { 3 String url ="uploadurl"; 4 file1 = "D:\\test.xls"; 5 fileName = "test.xls"; 6 7 String newLine = "\r\n"; 8 String boundaryPrefix = "--"; 9 String boundary = "----" + UUID.randomUUID().toString(); 10 11 URL postUrl = new URL(url); 12 HttpURLConnection conn = (HttpURLConnection) postUrl.openConnection(); 13 14 conn.setRequestMethod("POST"); 15 conn.setDoInput(true); 16 conn.setDoOutput(true); 17 conn.setUseCaches(false); 18 19 conn.setRequestProperty("connection", "Keep-Alive"); 20 conn.setRequestProperty("Charset", "UTF-8"); 21 conn.setRequestProperty("Content-Type", 22 "multipart/form-data; boundary=" + boundary); 23 24 OutputStream out = conn.getOutputStream(); 25 26 27 StringBuilder sb = new StringBuilder(); 28 29 sb.append(boundaryPrefix); 30 sb.append(boundary); 31 sb.append(newLine); 32 33 sb.append("Content-Disposition: form-data; name=\"id\""); 34 sb.append(newLine); 35 sb.append(newLine); 36 sb.append("testCodeUpload"); 37 sb.append(newLine); 38 39 sb.append(boundaryPrefix); 40 sb.append(boundary); 41 sb.append(newLine); 42 43 sb.append("Content-Disposition: form-data; name=\"name\""); 44 sb.append(newLine); 45 sb.append(newLine); 46 sb.append(fileName); 47 sb.append(newLine); 48 49 sb.append(boundaryPrefix); 50 sb.append(boundary); 51 sb.append(newLine); 52 53 sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"" 54 + fileName + "\""); 55 sb.append("Content-Type: application/octet-stream"); 56 sb.append(newLine); 57 sb.append(newLine); 58 59 out.write(sb.toString().getBytes()); 60 61 File file = new File(file1); 62 FileInputStream in = new FileInputStream(file); 63 byte[] bufferOut = new byte[1024]; 64 int bytes = 0; 65 while ((bytes = in.read(bufferOut)) != -1) { 66 out.write(bufferOut, 0, bytes); 67 } 68 out.write(newLine.getBytes()); 69 in.close(); 70 byte[] end_data = (newLine + boundaryPrefix + boundary + boundaryPrefix + newLine) 71 .getBytes(); 72 out.write(end_data); 73 out.flush(); 74 out.close(); 75 76 BufferedReader reader = new BufferedReader(new InputStreamReader( 77 conn.getInputStream())); 78 String line = null; 79 while ((line = reader.readLine()) != null) { 80 System.out.println(line); 81 } 82 }