文件的上传和下载,是非常常见的功能。很多的系统中,或者软件中都经常使用文件的上传和下载。 比如:QQ 头像,就使用了上传;邮箱中也有附件的上传和下载功能;OA 系统中审批有附件材料的上传
method=post
请求multipart/form-data
值input type=file
添加上传的文件enctype=multipart/form-data
表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器。
Servlet:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("文件上传过来了");
}
}
web.xml:
<servlet>
<servlet-name>UploadServletservlet-name>
<servlet-class>com.fox.servlet.UploadServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>UploadServletservlet-name>
<url-pattern>/uploadServleturl-pattern>
servlet-mapping>
上面我们只是简单地完成了文件上传的步骤,还没有在Servlet中解析上传的文件
要想完整实现文件上传,我们需要用到commons-fileupload.jar包,而commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入(可以在Maven仓库搜索下载)。
commons-fileupload.jar 中,我们常用的类和方法有哪些?
ServletFileUpload
类,用于解析上传的数据。
public static final boolean isMultipartContent(HttpServletRequest request)
判断当前上传的数据格式是否是多段的格式。public List parseRequest(HttpServletRequest request)
解析上传的数据FileItem类,表示每一个表单项。
boolean isFormField()
判断当前这个表单项,是普通的表单项(true);还是上传的文件类型(false)String getFieldName()
获取表单项的 name 属性值String getString()
获取当前表单项的value 属性值。String getName()
获取上传的文件名void write( file )
将上传的文件写到 参数 file 所指向的硬盘位置upload.jsp和web.xml还是和上面一样
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 {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
if(ServletFileUpload.isMultipartContent(request)){
//创建 FileItemFactory 工厂实现类
FileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类 ServletFileUpload 类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
try {
// 解析上传的数据,得到每一个表单项 FileItem
List<FileItem> list = servletFileUpload.parseRequest(request);
// 循环判断,每一个表单项,是普通类型,还是上传的文件
for (FileItem fileItem : list) {
if(fileItem.isFormField()){
// 普通表单项
System.out.println("表单项的name属性值:"+fileItem.getFieldName());
System.out.println("表单项的value属性值:"+fileItem.getString("UTF-8"));
}else{
//上传的文件
System.out.println("表单项的name属性值:"+fileItem.getFieldName());
System.out.println("上传的文件名:"+fileItem.getName());
//将上传的文件写入硬盘
fileItem.write(new File("d://test/"+fileItem.getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
response.getOutputStream()
返回适合在响应中写入二进制数据的Servlet输出流。servletContext.getResourceAsStream(String path)
将位于指定路径的资源作为输入流对象返回。servletContext.getMimeType(String file)
返回指定文件的MIME类型,如果MIME类型未知,则返回null。什么是MIME类型?response.setContentType(String type)
设置发送到客户端的响应的内容的MIME类型response.setHeader("Content-Disposition", "attachment; fileName=文件名")
这个响应头告诉浏览器,收到的数据是需要下载的。 attachment 表示附件,也就是下载的一个文件;fileName 表示下载的文件名。首先在web目录下新建一个file目录,并提前准备好一张图片
Servlet:
import org.apache.commons.io.IOUtils;
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;
public class DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取要下载的文件名
String downloadFileName="pyy.jpg";
//获取ServletContext对象
ServletContext servletContext = getServletContext();
//2、获取要下载的文件的MIME类型
//斜杠/被服务器解析表示地址为http://ip地址:端口号/工程名/ 映射到代码中来是web目录
String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
System.out.println("下载的文件类型:"+mimeType);
//3、回传前,通过设置响应头告诉客户端返回的MIME类型
response.setContentType(mimeType);
//4、回传前,通过设置响应头告诉客户端收到的数据是用于下载
//Content-Disposition响应头:表示收到的数据怎么处理
//attachment:附件的意思,表示用于下载
//filename:表示指定下载的文件名
response.setHeader("Content-Disposition","attachment; filename="+downloadFileName);
//5、读取要下载的文件内容(通过ServletContext对象可以读取)
InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
//获取响应的输出流
OutputStream outputStream = response.getOutputStream();
//6、把下载的文件内容回传给客户端
//copy方法读取输入流中全部的数据,复制给输出流,再输出给客户端
IOUtils.copy(resourceAsStream,outputStream);
}
}
web.xml:
<servlet>
<servlet-name>DownloadServletservlet-name>
<servlet-class>com.fox.servlet.DownloadServletservlet-class>
servlet>
<servlet-mapping>
<servlet-name>DownloadServletservlet-name>
<url-pattern>/downloadServleturl-pattern>
servlet-mapping>
完成上面的步骤,下载文件是没问题了。但是如果我们要下载的文件的文件名是中文的话,你会发现,下载的文件名不正确,是乱码。
原因是在响应头中,不能包含有中文字符,只能包含 ASCII 码。
那么如何解决中文乱码问题呢?
如果客户端浏览器是 IE 浏览器或者是谷歌浏览器,我们需要使用 URLEncoder 类先对中文名进行 UTF-8 的编码操作。这样,IE 浏览器和谷歌浏览器收到含有编码后的字符串后会以 UTF-8 字符集进行解码显示。
解决方案:
//把中文名进行 UTF-8 编码操作,然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition","attachment; filename=" + URLEncoder.encode("中文.jpg", "UTF-8"));
火狐浏览器用的是BASE64编码
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class Base64Test {
public static void main(String[] args) throws Exception {
String str="今天天气真好";
//创建一个Base64编码器
BASE64Encoder base64Encoder = new BASE64Encoder();
//执行Base64编码操作
String encodeStr = base64Encoder.encode(str.getBytes("UTF-8"));
//显示Base64编码后的结果
System.out.println(encodeStr);
//创建Base64解码器
BASE64Decoder base64Decoder = new BASE64Decoder();
//执行Base64解码操作
byte[] bytes = base64Decoder.decodeBuffer(encodeStr);
//显示Base64解码后的结果
String decodeStr = new String(bytes, "UTF-8");
System.out.println(decodeStr);
}
}
如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行 BASE64 的编码操作。
这时候需要把请求头 Content-Disposition: attachment; filename=中文名
改为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=
现在我们对这段内容: =?charset?B?xxxxx?=
进行一下说明:
=?
表示编码内容的开始charset
表示字符集B
表示BASE64编码xxxx
表示文件名BASE64编码后的内容?=
表示编码内容的结束解决方案:
// 使用固定格式进行 BASE64 编码后,设置到响应头中
response.setHeader("Content-Disposition","attachment; fileName=" + "=?UTF-8?B?" + new BASE64Encoder().encode("中文.jpg".getBytes("UTF-8")) + "?=");
12
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 DownloadServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、获取要下载的文件名
String downloadFileName="pyy.jpg";
//获取ServletContex对象
ServletContext servletContext = getServletContext();
//2、获取要下载的文件的MIME类型
//斜杠/被服务器解析表示地址为http://ip地址:端口号/工程名/ 映射到代码中来是web目录
String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
System.out.println("下载的文件类型:"+mimeType);
//3、回传前,通过设置响应头告诉客户端返回的MIME类型
response.setContentType(mimeType);
//4、回传前,通过设置响应头告诉客户端收到的数据是用于下载
//Content-Disposition响应头:表示收到的数据怎么处理
//attachment:附件的意思,表示用于下载
//filename:表示指定下载的文件名
//响应头User-Agent包含浏览器的信息(是哪个浏览器)
String ua = request.getHeader("User-Agent");
// 判断是否是火狐浏览器
if (ua.contains("Firefox")) {
// 使用固定格式进行 BASE64 编码后,设置到响应头中
response.setHeader("Content-Disposition","attachment; fileName=" + "=?UTF-8?B?" + new BASE64Encoder().encode("中文.jpg".getBytes("UTF-8")) + "?=");
}else {
//把中文名进行 UTF-8 编码操作,然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition","attachment; filename=" + URLEncoder.encode("中文.jpg", "UTF-8"));
}
//5、读取要下载的文件内容(通过ServletContext对象可以读取)
InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
//获取响应的输出流
OutputStream outputStream = response.getOutputStream();
//6、把下载的文件内容回传给客户端
//copy方法读取输入流中全部的数据,复制给输出流,再输出给客户端
IOUtils.copy(resourceAsStream,outputStream);
}
}