文件的上传和下载,是非常常见的功能。很多的系统中,或者软件中都经常使用文件的上传和下载。
比如:QQ头像,就使用了上传。
邮箱中也有附件的上传和下载功能。
OA系统中审批有附件材料的上传。
1、文件的上传介绍(重点)
- 要有一个 form 标签,method=post 请求
- form 标签的 encType 属性值必须为 multipart/form-data 值
- 在 form 标签中使用 input type=file 添加上传的文件
- 编写服务器代码(Servlet 程序)接收,处理上传的数据。
encType=multipart/form-data 表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器
1.1、文件上传,HTTP协议的说明
注意:谷歌浏览器中上传的文件的数据显示的是空行,但服务器可以接收到数据
1.2、commons-fileupload.jar 常用 API 介绍说明
commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入。
第一步,就是需要导入两个 jar 包: (fileupload包依赖io包)
- commons-fileupload-1.2.1.jar
- commons-io-1.4.jar
两个jar包中常用的类 (导入的jar包是commons的)
ServletFileUpload类,用于解析上传的数据
public static final boolean isMultipartContent(HttpServletRequest request)
- 如果上传的数据是多段的形式,返回true,只有多段的数据才是文件上传的
public ServletFileUpload()
- 空参构造器
public ServletFileUpload(FileItemFactory fileItemFactory)
- 参数为工厂实现类的构造器
public List parseRequest(HttpServletRequest request)
- 解析上传的数据,返回包含每一个表单项的List集合
FileItem类,表示每一个表单项
public boolean isFormField()
- 如果当前表单项是普通表单项,返回true,如果上传的文件类型则返回false
public String getFieldName()
- 获取当前表单项的name属性值
public String getString()
- 获取当前表单项的value属性值,参数为”UTF-8”可解决乱码问题
public String getName()
- 获取上传的文件名
public void write(File file)
- 将上传的文件写到参数File所指向的硬盘位置
代码实现:
upload.jsp
<%--
Created by IntelliJ IDEA.
User: DELL
Date: 2021/11/13
Time: 22:13
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/jsp/uploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username" ><br>
头像:<input type="file" name="photo" ><br>
<input type="submit" value="上传">
</form>
</body>
</html>
UploadServlet.java
package com.chang.servlet;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;
public class UploadServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
if(ServletFileUpload.isMultipartContent(req)){
// 创建FileItemFactory工厂实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类ServletFileUpload类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,得到每一个表单项FileItem
List<FileItem> list = servletFileUpload.parseRequest(req);
// 循环判断,每一个表单项,是普通类型,还是上传的文件
for (FileItem fileItem : list) {
if (fileItem.isFormField()) {
// 普通表单项
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
// 参数UTF-8.解决乱码问题
System.out.println("表单项的value属性值:" + fileItem.getString("UTF-8"));
} else {
// 上传的文件
System.out.println("表单项的name属性值:" + fileItem.getFieldName());
System.out.println("上传的文件名:" + fileItem.getName());
fileItem.write(new File("e:\\" + fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.chang.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/uploadServlet</url-pattern>
</servlet-mapping>
</web-app>
2、文件的下载过程
- 获取要下载的文件名
- 获取要下载的文件类型
- 将获取的文件类型告诉客户端
- 告诉客户端收到的数据用于下载使用
- 获取要下载的文件并回传给客户端
2.1、文件下载过程详解
- 获取要下载的文件名:使用String定义要下载的文件名
获取要下载的文件类型:
通过ServletContext的`**getMimeType()**`参数是要下载的文件所在路径,返回值是String类型
将获取的文件类型告诉客户端:
通过response的`**setContentType()**`参数是第二步的结果,无返回值
告诉客户端收到的数据用于下载使用(没有此步则内容直接显示在页面上):
通过response.`**setHeader()**`
参数是**"Content-Disposition", “attachment; fileName=xxx.xxx"**
注意:
Content-Disposition 响应头表示客户端收到的数据如何处理
attachment 表示附件,用于下载
filename 表示下载的文件名,可以与原文件名不同
获取要下载的文件并回传给客户端:
回传给客户端通过导入的io包的IOUtils.`copy(InputStream input, OutputStream output)`
通过ServletContext的`getResourceAsStream()`参数是要下载的文件路径,得到输入流
通过response.`getOutputStream()`得到响应的输出流
2.2、中文名下载文件的乱码问题
原因
response.setHeader(“Content-Disposition”, “attachment; fileName=中文名.jpg”);
如果下载的文件是中文名,会发现下载的文件无法正常显示汉字,原因是响应头中不能有汉字
解决
当浏览器是IE浏览器或谷歌浏览器:
需要使用URLEncoder类先对中文名进行UTF-8编码,因为IE浏览器和谷歌浏览器收到含有 编码的字符串后会以UTF-8字符集进行解码显示
当浏览器是火狐浏览器:使用BASE64编解码
BASE64编解码
代码实现:
DownLoad.java
package com.chang.servlet;
import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Encoder;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
public class DownLoad extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1、获取要下载的文件名
String downloadFileName = "2.png";
//2、读取要下载的文件内容 (通过ServletContext对象可以读取)
ServletContext servletContext = getServletContext();
//3、获取要下载的文件类型
String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
System.out.println("下载的文件类型:" + mimeType);
//4、在回传前,通过响应头告诉客户端返回的数据类型
resp.setContentType(mimeType);
//5、还要告诉客户端收到的数据是用于下载使用(还是使用响应头)
// Content-Disposition响应头,表示收到的数据怎么处理
// attachment表示附件,表示下载使用
// filename= 表示指定下载的文件名
// url编码是把汉字转换成为%xx%xx的格式
if (req.getHeader("User-Agent").contains("Firefox")) {
// 如果是火狐浏览器使用Base64编码
resp.setHeader("Content-Disposition", "attachment; filename==?UTF-8?B?" + new BASE64Encoder().encode("中国.jpg".getBytes("UTF-8")) + "?=");
} else {
// 如果不是火狐,是IE或谷歌,使用URL编码操作
resp.setHeader("Content-Disposition", "attachment; filename=" + downloadFileName);
// resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("中国.jpg", "UTF-8"));
}
//只有后面三行代码可以实现在浏览器中获取请求的文件资源,因此需要前面的代码告知如何处理
//斜杠被服务器解析表示地址为http://ip:port/工程名 映射 到代码的web目录
InputStream resourceAsStream=servletContext.getResourceAsStream("/file/"+downloadFileName);
//获取相应的输出流
OutputStream outputStream= resp.getOutputStream();
//使用commons-io这个包中的IOUtils实现读入与写出
IOUtils.copy(resourceAsStream,outputStream);
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>printHtml</servlet-name>
<servlet-class>com.chang.servlet.PrintHtml</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>printHtml</servlet-name>
<url-pattern>/printHtml</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>com.chang.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/uploadServlet</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>download</servlet-name>
<servlet-class>com.chang.servlet.DownLoad</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>download</servlet-name>
<url-pattern>/download</url-pattern>
</servlet-mapping>
</web-app>