1 文件上传
1.1 文件的上传步骤
1,需要一个form标签,method=post
(采用post的方式是因为get的长度有限)
2,form标签的enctype属性的值必须是 multipart/form-data
(代表上传的数据以多段的形式拼接,然后以二进制流的形式发送到服务器)
3,form标签内需要一个<input type="file">来选择需要上传的文件
4,在服务器端接收二进制流并处理
1.2 文件上传时的HTTP协议内容
页面中的上传文件表单:
<form action="/MyServlet2" method="post" enctype="multipart/form-data">
用户名 <input type="text" name="userName"> <br>
头 像 <input type="file" name="photo"> <br>
<input type="submit" value="上传">
</form> <br>
上传表单的使用:
提交后在Chrome浏览器中的HTTP协议内容:
POST /MyServlet1 HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 293
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:8080
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryBpBL5EuHkTg5qb4m
// 表示提交的数据类型 multipart/form-data表示提交的数据以多段的形式进行拼接,然后以二进制流的形式发送给服务器
// boundary表示每段数据的分隔符,----WebKitFormBoundaryBpBL5EuHkTg5qb4m由浏览器随机生成,是数据的分隔符
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:8080/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Idea-ce0ce528=48e61e25-191c-4d62-8bfb-19417bc25f46; JSESSIONID=28C7E0C3069342202B9033E9B469C1F9
// 每两个分隔符之间就是表单中的一个表单项
------WebKitFormBoundaryBpBL5EuHkTg5qb4m
Content-Disposition: form-data; name="userName"
Coisini
------WebKitFormBoundaryBpBL5EuHkTg5qb4m
Content-Disposition: form-data; name="photo"; filename=""
Content-Type: application/octet-stream
// 这里是文件的内容但是过长被Chrome省略
------WebKitFormBoundaryBpBL5EuHkTg5qb4m--
当提交数据后,表单中的内容将会被分段并转化为二进制流传输到服务器,服务器接收到的二进制流就是表单中的内容(包括普通表单项和上传文件表单项)
接下来需要对这串二进制流进行处理:
1,将二进制流转换为表单项数据
2,将普通表单项和上传文件表单项分类处理
3,将上传文件表单项中的文件写入服务器的磁盘
1.3 解析上传的流
解析这个二进制流,可以采用commons-fileupload.jar和commons-io.jar来完成,这两个jar文件内提供了现成的工具
jar文件的Maven依赖:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
对二进制流的处理需要在一个Servlet程序内的doPost方法内进行:
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
/*
* 首先判断本次请求是否是多段数据请求
* 如果是则说明本次请求中会上传文件,需要处理二进制流
* 不是则说明本次请求处理一个普通的表单
* */
if(ServletFileUpload.isMultipartContent(request)) {
/*处理带文件的二进制流*/
// 1,创建用于解析二进制流的ServletFileUpload对象
FileItemFactory fileItemFactory = new DiskFileItemFactory();
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 2,解析二进制流获得一个表单项对象列表
List<FileItem> fileItems = servletFileUpload.parseRequest(request);
// 3,将表单项对象列表分类,分成普通表单项和上传文件表单项分类处理
for(FileItem fileItem : fileItems) {
// 处理普通表单项
if(fileItem.isFormField()) {
// 输出该表单项的name属性
System.out.println(fileItem.getFieldName());
// 输出该表单项的值 可以解决中文乱码的问题
System.out.println(fileItem.getString("UTF-8"));
// 处理上传文件表单项
} else {
// 输出该表单项的name属性
System.out.println(fileItem.getFieldName());
// 输出文件名
System.out.println(fileItem.getName());
// 将该文件写到本地磁盘
fileItem.write(new File("D:\\" + fileItem.getName()));
}
}
} catch (Exception e) {}
} else {
/*处理普通表单*/
}
}
上传文件过程:
1,判断本次请求处理的是普通表单还是带上传文件的表单,普通表单则正常处理
2,创建用于解析表单的ServletFileUpload类对象
3,通过ServletFileUpload类对象解析request对象,获取表单项列表
4,遍历表单项列表,将普通表单项和带上传文件的表单项分开处理
5,将带上传文件的表单项通过FileItem.write(new File("路径"))写入本地磁盘
2 文件下载
2.1 下载文件的步骤
服务器端:
1,获取表单中的想要下载的文件的文件名
2,获取ServletContext对象,根据文件名获得该文件的输入流
3,通过response对象获取响应输出流
4,更改响应头,告知客户端下载文件的数据类型(MIME格式)
5,更改响应头,告知客户端收到的数据是从服务器下载的文件
6,将文件输入流中的内容写入响应输出流,完成下载
2.2 下载文件
在客户端下载表单中输入想要下载的文件:
服务器端的处理:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 1,获取客户端想要下载的文件名
String downloadFileName = request.getParameter("fileName");
// 2,获取ServletContext对象,并取得目标文件的输入流
ServletContext servletContext = getServletContext();
InputStream inputStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
// 3,通过response对象获取响应流
OutputStream outputStream = response.getOutputStream();
// 4,更改响应头,告知客户端返回数据的MIME格式
String fileType = servletContext.getMimeType("/file/" + downloadFileName);
response.setContentType(fileType);
// 5,更改响应头,告知客户端本次响应收到的数据是从服务器下载的文件
response.setHeader("Content-Disposition", "attachment;filename");
// 6,将文件输入流写入响应输出流,完成下载
IOUtils.copy(inputStream, outputStream);
}
在第5步更改响应头,告知客户端本次收到的数据是从服务器下载的文件时:
response.setHeader("Content-Disposition", "attachment;filename");
Content-Disposition 表示接收到的数据怎么处理
attachment; 表示作为下载文件
filename 表示下载文件在客户端显示的文件名称
filename默认为服务器端下载文件的文件名,也可以更改filename的值
response.setHeader("Content-Disposition",
"attachment;filename=" + URLEncoder.encode("素材.png", "UTF-8"));
但是如果想写成中文的话,在Chrome和IE上需要进行URL编码
在火狐上需要进行Base64编码