当前位置:   article > 正文

HTTP协议之multipart/form-data请求分析_httputil.get拼上multipart参数

httputil.get拼上multipart参数

HTTP协议之multipart/form-data请求分析

问题引出

在解决文件上传附带参数的时候,发现当form中添加enctype:"multipart/form-data",后台用request.getParameter(“XXX”)获取不到数据

原因分析:
1.get方式
get方式提交的话,表单项都保存在http header中,格式是
http://localhost:8080/hello.do?name1=value1&name2=value2这样的字符串。server端通过request.getParameter是可以取到值的。
2.post方式(enctype为缺省的application/x-www-form-urlencoded)
表单数据都保存在http的正文部分,格式类似于下面这样:用request.getParameter是可以取到数据的

name1=value1&name2=value2

3.post方式(enctype为multipart/form-data,多用于文件上传,对于只想传value的做法,显然使用application/json或者text/plain会好很多。可以把数据放到form的header或者body中,在后台使用相应的方法得到具体值)
表单数据都保存在http的正文部分,各个表单项之间用boundary隔开。格式类似于下面这样:用request.getParameter()是取不到数据的,这时需要通过request.getInputStream来取数据,不过取到的是个InputStream,所以无法直接获取指定的表单项(需要自己对取到的流进行解析,才能得到表单项以及上传的文件内容等信息)。这种需求属于比较共通的功能,所以有很多开源的组件可以直接利用。比如:apache的fileupload组件,smartupload等。通过这些开源的upload组件提供的API,就可以直接从request中取得指定的表单项了。

什么是multipart/form-data请求:

1、multipart/form-data的基础方法是post,也就是说是由post方法来组合实现的
2、multipart/form-data与post方法的不同之处:请求头,请求体。
3、multipart/form-data的请求头必须包含一个特殊的头信息:Content-Type,且其值也必须规定为multipart/form-data,同时还需要规定一个内容分割符用于分割请求体中的多个post的内容,如文件内容和文本内容自然需要分割开来,不然接收方就无法正常解析和还原这个文件了。具体的头信息如下:

Content-Type: multipart/form-data; boundary=${bound}

//其中${bound} 是一个占位符,代表我们规定的分割符,可以自己任意规定,但为了避免和正常文本重复了,尽量要使用复杂一点的内容。如:--------------------56423498738365

4、multipart/form-data的请求体也是一个字符串,不过和post的请求体不同的是它的构造方式,post是简单的name=value值连接,而multipart/form-data则是添加了分隔符等内容的构造体。具体格式如下:
--${bound}  
Content-Disposition: form-data; name="Filename"  
 
HTTP.pdf  
--${bound}  
Content-Disposition: form-data; name="file000"; filename="HTTP协议详解.pdf"  
Content-Type: application/octet-stream  
 
%PDF-1.5  
file content  
%%EOF  
 
--${bound}  
Content-Disposition: form-data; name="Upload"  
 
Submit Query  
--${bound}--  
其中${bound}为之前头信息中的分割符,如果头信息中规定为123,那么这里也要为123,;可以很容易看出,这个请求体是多个相同的部分组成的:每一个部分都是以--加分隔符开始的,然后是该部分内容的描述信息,然后一个回车,然后是描述信息的具体内容;如果传送的内容是一个文件的话,那么还会包含文件名信息,以及文件内容的类型。上面的第二个小部分其实是一个文件体的结构,最后会以--分割符--结尾,表示请求体结束。

综上,可以知道要发送一个multipart/form-data的请求,其实任何支持post请求的工具或语言都可以支持,只是自己要稍微包装一下便可。

form表单设置enctype="multipart/form-data"后获取参数

方案一:JS修改上传路径


  1. <form id="upload" name="upload" action="fileftp.jsp" method="post" ENCTYPE="multipart/form-data">
  2. <input type="hidden" name="otherName" id="otherName" value="abcdefg"/>
  3. <td nowrap>
  4. <input type="file" id="file1" name="file1" value="" size="40" class="sbttn"/>
  5. <input type="submit" value="上传" class="sbttn"/>
  6. </td>
  7. </form>

当表单提交到fileftp.jsp后,是无法使用request.getParameter("otherName")获得到abcdefg的值的。但是可以获得到file1的文件。
这里使用JS在表单提交的时候将参数拼接在表单action地址的后面。代码像这样的:


  1. <script language="javascript">
  2. function formSubmit(){
  3. var action="fileftp.jsp";
  4. action+="?otherName="+document.upload.otherName.value;
  5. document.upload.action=action;
  6. document.upload.submit();
  7. }
  8. </script>

第二个示例


  1. <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  2. <%
  3. String path = request.getContextPath();
  4. String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  5. %>
  6. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  7. <html>
  8. <head>
  9. <base href="<%=basePath%>">
  10. <title>上传文件开通业务</title>
  11. <meta http-equiv="pragma" content="no-cache">
  12. <meta http-equiv="cache-control" content="no-cache">
  13. <meta http-equiv="expires" content="0">
  14. <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  15. <meta http-equiv="description" content="This is my page">
  16. <!--
  17. <link rel="stylesheet" type="text/css" href="styles.css">
  18. -->
  19. </head>
  20. <script type="text/javascript">
  21. function pidImport() {
  22. var url = "/lobby/view/product/doUpload.jsp?";
  23. var inputList = document.getElementsByTagName("input");
  24. var pid = document.upload.pid.value;
  25. url += "pid=" + pid;
  26. document.all.upload.action = url;
  27. document.all.upload.submit();
  28. }
  29. </script>
  30. <body>
  31. <form name="upload" action="/lobby/view/product/doUpload.jsp" method="post" enctype="multipart/form-data">
  32. 产品id: <input type="text" name="pid" size="10"/><br/>
  33. 选择文件:<input type="file" name="upfile" size="50"/><br/>
  34. <input type="button" value="提交" οnclick="pidImport();">
  35. </form>
  36. </body>
  37. </html>

接收的时候直接使用reqeust.getParameter("pid")即可
方案二:使用开源fileupload
程序要求:
1、提供一个 HTML 文件,用户可在相应表单中选择需要上传的文件;
2、编写一个名叫 UploadServlet 的Servlet 文件,主要功能是解析上面 HTML 表单所提交的 HTTP 请求,把普通的文本域和文件域分离开来;
3、UploadServlet 根据 web.xml 配置文件中的初始化参数确定好需要在 Web 服务器存放该文件的目录,将上传的文件写入到该存放目录中,在我的这里我把上传的文件保存到 F:\java\JavaWeb\ch05\uploadFile 目录中,而 F:\java\JavaWeb\ch05\temp 目录则用作该 Web 应用的临时目录。
程序代码:
upload.html 文件

  1. <html>
  2. <head> <title>Servlet 上传文件</title></head>
  3. <body >
  4. <form name="uploadForm" method="POST"
  5. enctype="MULTIPART/FORM-DATA"
  6. action="upload">
  7. User Name:<input type="text" name="username" size="30"/>
  8. Upload File1:<input type="file" name="file1" size="30"/>
  9. Upload File2:<input type="file" name="file2" size="30"/>
  10. <input type="submit" name="submit" value="上传">
  11. <input type="reset" name="reset" value="重置">
  12. </form>
  13. </body>
  14. </html>

UploadServlet.java 文件

  1. import javax.servlet.*;
  2. import javax.servlet.http.*;
  3. import java.io.*;
  4. import java.util.*;
  5. import org.apache.commons.fileupload.*;
  6. import org.apache.commons.fileupload.servlet.*;
  7. import org.apache.commons.fileupload.disk.*;
  8. // Servlet 文件上传
  9. public class UploadServlet extends HttpServlet
  10. {
  11. private String filePath; // 文件存放目录
  12. private String tempPath; // 临时文件目录
  13. // 初始化
  14. public void init(ServletConfig config) throws ServletException
  15. {
  16. super.init(config);
  17. // 从配置文件中获得初始化参数
  18. filePath = config.getInitParameter("filepath");
  19. tempPath = config.getInitParameter("temppath");
  20. ServletContext context = getServletContext();
  21. filePath = context.getRealPath(filePath);
  22. tempPath = context.getRealPath(tempPath);
  23. System.out.println("文件存放目录、临时文件目录准备完毕 ...");
  24. }
  25. // doPost
  26. public void doPost(HttpServletRequest req, HttpServletResponse res)
  27. throws IOException, ServletException
  28. {
  29. res.setContentType("text/plain;charset=gbk");
  30. PrintWriter pw = res.getWriter();
  31. try{
  32. DiskFileItemFactory diskFactory = new DiskFileItemFactory();
  33. // threshold 极限、临界值,即硬盘缓存 1M
  34. diskFactory.setSizeThreshold(4 * 1024);
  35. // repository 贮藏室,即临时文件目录
  36. diskFactory.setRepository(new File(tempPath));
  37. ServletFileUpload upload = new ServletFileUpload(diskFactory);
  38. // 设置允许上传的最大文件大小 4M
  39. upload.setSizeMax(4 * 1024 * 1024);
  40. // 解析HTTP请求消息头
  41. List fileItems = upload.parseRequest(req);
  42. Iterator iter = fileItems.iterator();
  43. while(iter.hasNext())
  44. {
  45. FileItem item = (FileItem)iter.next();
  46. if(item.isFormField())
  47. {
  48. System.out.println("处理表单内容 ...");
  49. processFormField(item, pw);
  50. }else{
  51. System.out.println("处理上传的文件 ...");
  52. processUploadFile(item, pw);
  53. }
  54. }// end while()
  55. pw.close();
  56. }catch(Exception e){
  57. System.out.println("使用 fileupload 包时发生异常 ...");
  58. e.printStackTrace();
  59. }// end try ... catch ...
  60. }// end doPost()
  61. // 处理表单内容
  62. private void processFormField(FileItem item, PrintWriter pw)
  63. throws Exception
  64. {
  65. String name = item.getFieldName();
  66. String value = item.getString();
  67. pw.println(name + " : " + value + "\r\n");
  68. }
  69. // 处理上传的文件
  70. private void processUploadFile(FileItem item, PrintWriter pw)
  71. throws Exception
  72. {
  73. // 此时的文件名包含了完整的路径,得注意加工一下
  74. String filename = item.getName();
  75. System.out.println("完整的文件名:" + filename);
  76. int index = filename.lastIndexOf("\\");
  77. filename = filename.substring(index + 1, filename.length());
  78. long fileSize = item.getSize();
  79. if("".equals(filename) && fileSize == 0)
  80. {
  81. System.out.println("文件名为空 ...");
  82. return;
  83. }
  84. File uploadFile = new File(filePath + "/" + filename);
  85. item.write(uploadFile);
  86. pw.println(filename + " 文件保存完毕 ...");
  87. pw.println("文件大小为 :" + fileSize + "\r\n");
  88. }
  89. // doGet
  90. public void doGet(HttpServletRequest req, HttpServletResponse res)
  91. throws IOException, ServletException
  92. {
  93. doPost(req, res);
  94. }
  95. }


web.xml 文件


  1. <?xml version="1.0" encoding="gb2312"?>
  2. <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  5. version="2.4">
  6. <servlet>
  7. <servlet-name>UploadServlet</servlet-name>
  8. <servlet-class>UploadServlet</servlet-class>
  9. <init-param>
  10. <param-name>filepath</param-name>
  11. <param-value>uploadFile</param-value>
  12. </init-param>
  13. <init-param>
  14. <param-name>temppath</param-name>
  15. <param-value>temp</param-value>
  16. </init-param>
  17. </servlet>
  18. <servlet-mapping>
  19. <servlet-name>UploadServlet</servlet-name>
  20. <url-pattern>/upload</url-pattern>
  21. </servlet-mapping>
  22. </web-app>

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/586683
推荐阅读
相关标签
  

闽ICP备14008679号