前言

对于这块知识点,我一直都是模糊的,不是非常清楚的。在平时的工作中,遇到上传的问题,也没有深入的去研究过,也都是直接用别人封装好的类来完成自己的工作。某一天,看了本书,说到这个知识点,一脸茫然,觉的有必要去深入的学习一下,至少要让自己明白HTTP文件上传的过程,这个原理,以便将来出现问题,也能通过原理进行深入的分析,而不是等问题来了,两眼一抹黑,然后漫无目的的百度。哦,如果是那样子,那该是多么的痛苦,多么的无助。

所以查缺补漏,以免让将来的自己感到无助、痛苦,甚至难堪,走起!!!

HTTP上传原理

我们在开发的时候,当要用到文件上传功能时,前端开发人员都会告诉你以下几条金科律令:

  • 提交方式必须为post
  • 表单中有文件上传的表单项必须为<input type="file"/>
  • 必须指定表单类型enctype="multipart/form-data"

是的,我们必须按照上面这三条铁令进行设定,否则就无法上传文件。比如我们一般会这么写:

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<body>
<form action="<%=request.getContextPath() %>/UploadServletDemo" enctype="multipart/form-data" method="post">
上传用户:<input type="text" name="username"><br/>
上传文件1:<input type="file" name="file1"><br/>
上传文件2:<input type="file" name="file2"><br/>
 
<input type="submit" value="提交">
</form>
</body>
</html>

我这里写了一个简单的Servlet:

@WebServlet("/UploadServletDemo")
public class UploadServletDemo extends HttpServlet{
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8"); // 获取表单(POST)数据
ServletInputStream in = request.getInputStream();//此方法得到所有的提交信息,不仅仅只有内容 // 转换流
InputStreamReader inReaser = new InputStreamReader(in); // 缓冲流
BufferedReader reader = new BufferedReader(inReaser);
String str = null;
while ((str=reader.readLine()) != null){
System.out.println(str);
}
}
}

我们把程序跑起来,然后通过Fiddler进行抓包,可以看到我们发送的Post请求中,请求体中有以下这样的数据:

POST http://localhost:8080/javawebservlet_war/UploadServletDemo HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 446
Cache-Control: max-age=0
Origin: http://localhost:8080
Upgrade-Insecure-Requests: 1
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqj67FUBQUHXZj78G
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Referer: http://localhost:8080/javawebservlet_war/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,und;q=0.7,zh-TW;q=0.6
Cookie: JSESSIONID=6BE280EF3CBE213F73430FFDF015DE97 ------WebKitFormBoundaryqj67FUBQUHXZj78G
Content-Disposition: form-data; name="username" abc
------WebKitFormBoundaryqj67FUBQUHXZj78G
Content-Disposition: form-data; name="file1"; filename="文件1.txt"
Content-Type: text/plain ABC文件1
------WebKitFormBoundaryqj67FUBQUHXZj78G
Content-Disposition: form-data; name="file2"; filename="文件2.txt"
Content-Type: text/plain BDF文件2
------WebKitFormBoundaryqj67FUBQUHXZj78G--

到这里,我们就大概就知道了HTTP上传文件的原理了。HTTP把需要上传的表单的所有数据按照一定的格式存放在请求体中,对于文件也是同样的。

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqj67FUBQUHXZj78G表示要上传附件,其中boundary表示分隔符,如果表单中有多项,就要使用boundary进行分隔,每个表单项由------FormBoundary开始,以------FormBoundary结尾。例如这样:

------FormBoundary
Content-Disposition: form-data; name="param1" value1
------FormBoundary

这个boundary的值是由浏览器生成的,由浏览器来保证与上传内容不重复。在每个分隔项里,需要我们去重点关注Content-Disposition消息头,其中第一个参数总是固定不变的form-data,name表示表单元素属性名,回车换行符后面的内容就是元素的值。还有Content-Type表示我们上传的文件的MIME类型,我们在服务器端需要根据这个进行文件的区分。

HTTP就是按照这种格式,把表单中的数据封装成一个请求一股脑的发给服务器端,服务器端根据这种规则对接收到的请求进行解析,从而完成文件上传功能。

最后一个boundary的结尾会多两个--

FileUpload组件

通过上面的描述,我们可以知道完成文件上传功能,重点工作不是在于客户端,而是在于服务器端。服务器端需要根据客户端发送过来的请求,根据上面说的规则对请求报文进行解析,从而提取出上传的文件内容。可以看到,虽然上面的规则比较简单,但是用不同的开发语言来一次性写出没有Bug的解析程序,也不是那么简单的。而且这种实现过一遍,就可以大家共享的东西就非常适合开发成组件供大家一起使用,所以呢,开源社区就开发了这样的一个组件,这个组件来给我们完成了上面规则的编码,而我们需要做的就是去学会使用这个组件,就这么简单!

下面就通过一个简单的FileUploadDemo程序来总结一下如何使用FileUpload组件。

FileUpload Demo

下面来一段简单的使用Demo。

@WebServlet("/FileUpload")
public class FileUploadDemo extends HttpServlet {
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
DiskFileItemFactory fac = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setFileSizeMax(10 * 1024 * 1024);
upload.setSizeMax(20 * 1024 * 1024); if (upload.isMultipartContent(request)) {
try {
List<FileItem> list = upload.parseRequest(request);
for (FileItem item : list) {
if (item.isFormField()) {
String fileName = item.getFieldName();
String value = item.getString("UTF-8");
System.out.println(fileName + ":" + value);
} else {
String name = item.getName();
String id = UUID.randomUUID().toString();
name = id + name; String realPath = getServletContext().getRealPath("/upload");
File file = new File(realPath, name);
item.write(file);
item.delete(); }
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("不处理!");
}
}
}

总结

这篇文章对通过HTTP协议进行文件上传原理进行了比较详细的分析和总结,希望对大家有帮助!

2019年7月31日 于内蒙古呼和浩特。

HTTP文件上传原理的更多相关文章

  1. Spring Boot 文件上传原理

    首先我们要知道什么是Spring Boot,这里简单说一下,Spring Boot可以看作是一个框架中的框架--->集成了各种框架,像security.jpa.data.cloud等等,它无须关 ...

  2. php文件上传原理详解(含源码)

    1.文件上传原理 将客户端的文件上传到服务器,再将服务器的临时文件上传到指定目录 2.客户端配置 提交表单 表单的发送方式为post 添加enctype="multipart/form-da ...

  3. 补习系列(11)-springboot 文件上传原理

    目录 一.文件上传原理 二.springboot 文件机制 临时文件 定制配置 三.示例代码 A. 单文件上传 B. 多文件上传 C. 文件上传异常 D. Bean 配置 四.文件下载 小结 一.文件 ...

  4. Java Web文件上传原理分析(不借助开源fileupload上传jar包)

    Java Web文件上传原理分析(不借助开源fileupload上传jar包) 博客分类: Java Web   最近在面试IBM时,面试官突然问到:如果让你自己实现一个文件上传,你的代码要如何写,不 ...

  5. Struts2单文件上传原理及示例

    一.文件上传的原理 表单元素的enctype属性指定的是表单数据的编码方式,该属性有3个值: 1.application/x-www-form-urlencoded:这是默认编码方式,它只处理表单域里 ...

  6. 常见的文件上传方法有哪些?Ajax文件上传原理是什么?

    Ajaxfileupload,Ajaxupload,JqueryUploadify无刷新式的文件上传,在一个页面里嵌入一个Iframe,然后在Iframe使用原生的Post表单提交.

  7. js文件上传原理(form表单 ,FormData + XHR2 + FileReader + canvas)

    目录 form表单上传 FormData + XHR2 + FileReader + canvas 无刷新本地预览压缩上传实例 目前实现上传的方式 浏览器小于等于IE9(低版本浏览器)使用下面的方式实 ...

  8. Struts2多文件上传原理和示例

    一.创建上传文件的页面,代码如下所示     1.Struts2也可以很方便地实现多文件上传. 在输入表单域增加多个文件域:multifileupload.jsp    <%@ page lan ...

  9. HTTP上传 文件上传 图片上传 HTTP上传原理 文件上传原理 图片上传原理

    1.概述 在最初的http协议中,没有上传文件方面的功能.rfc1867(http://www.ietf.org/rfc/rfc1867.txt )为http协议添加了这个功能.浏览器按照此规范将用户 ...

随机推荐

  1. 丢给你一个txt并同时获取你shell

    丢给你一个txt并同时获取你shell 0x00:回顾 <文本编辑器Vim/Neovim被曝任意代码执行漏洞> 听闻很多人知道这个漏洞,但是有一部分人能复现成功,一部分人复现不出来.这里我 ...

  2. java基础知识总结(二)

    Java中的代码块 java中的代码块是用{}括起来的代码,进行一些功能的限定 静态代码块:在类第一次被初始化的是后执行,负责一些类的初始化操作,仅仅只执行一次 构造代码块:顾名思义,辅助构造器进行初 ...

  3. CI框架使用(一)

    CI框架的使用是很简单,也 是mvc模式.其中有好多类直接调用.   在使用帮助函数的时候,都需要手动加载,或者是在配置文件中加一个自动加载 $this->load->helper('ur ...

  4. Linux搭建基于Apache的HTTP服务器

    Linux搭建基于Apache的HTTP服务器   实验目标: 通过本实验掌握基于Linux的WWW服务器搭建. 实验步骤: 1.安装http服务 2.防火墙放通http服务 3.编辑测试网页 4.开 ...

  5. 2018.10.20 2018NOIP冲刺之酒厂选址

    题目传送门 明显能够看出有一个建图求路程的优化 然而发现10000*10000爆空间QAQ 为了做一些初始化方面的优化 我们发现了一个叫做前缀和的东西 可以在环上查到两个之间的最短距离 同时还要做一些 ...

  6. POJ 3692:Kindergarten(二分图最大团)

    题目链接 题意 已知班级有g个女孩和b个男孩,所有女生之间都相互认识,所有男生之间也相互认识,给出m对关系表示哪个女孩与哪个男孩认识.现在要选择一些学生来组成一个集合,使得里面所有人都认识,求此集合最 ...

  7. java html中文汉字 反转义

    import org.apache.commons.lang.StringEscapeUtils; public class EscapeHtml { /** * @param args */ pub ...

  8. 聚类时的轮廓系数评价和inertia_

    在进行聚类分析时,机器学习库中提供了kmeans++算法帮助训练,然而,根据不同的问题,需要寻找不同的超参数,即寻找最佳的K值 最近使用机器学习包里两个内部评价聚类效果的方法:clf=KMeans(n ...

  9. 【Aizu - 0121】Seven Puzzle (反向bfs)

    -->Seven Puzzle 原文是日语 这里就直接写中文了  Descriptions: 7拼图由8个正方形的卡和这些卡片完全收纳的框构成.每张卡都编号为0, 1, 2, …, 7,以便相互 ...

  10. C++学习书籍推荐《Effective C++ 第三版》下载

    百度云及其他网盘下载地址:点我 编辑推荐 <Effective C++:改善程序与设计的55个具体做法(第3版)(中文版)(双色)>前两个版本抓住了全世界无数程序员的目光.原因十分明显:S ...