- 文件的上传和下载在web应用中是非常常用,也是非常有用的功能。
- 例如:发送电子邮件时可以同过上传附件发送文件,OA系统中可以通过上传文件来提交公文,社交网站通过上传图片来自定义头像等等。
- 例如:下载实际上只要资源放在用户可访问的目录中用户就可以直接通过地址下载,但是一些资源是存放到数据库中的,还有一些资源需要一定权限才能下载,这里就需要我们通过Servlet来完成下载的功能。
- 可以说上传和下载是每一个web应用都需要具有的一个功能,所以需要我们掌握。
文件的上传主要分成两个步骤:
用户在页面中选择要上传的文件,然后将请求提交到Servlet
Servlet收到请求,解析用户上传的文件,然后将文件存储到服务器
创建一个form表单
<form action="FileUploadDemoServlet" method="post" enctype="multipart/form-data">
上传文件名 : <input type="text" name="username"> <br>
上传文件 : <input type="file" name="ufile"> <br>
<input type="submit" value="上传文件">
</form>
commons-fileupload是Apache开发的一款专门用来处理上传的工具,它的作用就是可以从request对象中解析出,用户发送的请求参数和上传文件的流。
commons-fileupload包依赖commons-io,两个包需要同时导入。
核心类:
DiskFileItemFactory
工厂类,用于创建ServletFileUpload,设置缓存等
该类一般直接使用构造器直接创建实例
方法:
public void setSizeThreshold(int sizeThreshold):用于设置缓存文件的大小(默认值10kb)
public void setRepository(File repository):用于设置缓存文件位置(默认系统缓存目录)
ServletFileUpload
该类用于解析request对象从而获取用户发送的请求参数(包括普通参数和文件参数)
该类需要调用有参构造器创建实例,构造器中需要一个DiskFileItemFactory作为参数
方法:
public List parseRequest(HttpServletRequest request):解析request对象,获取请求参数,返回的是一个List,List中保存的是一个FileItem对象,一个对象代表一个请求参数。
public void setFileSizeMax(long fileSizeMax):设置单个文件的大小限制,单位为B。如果上传文件超出限制,会在parseRequest()抛出异常FileSizeLimitExceededException。
public void setSizeMax(long sizeMax):限制请求内容的总大小,单位为B。如果上传文件超出限制,会在parseRequest()抛出异常SizeLimitExceededException。
FileItem
该类用于封装用户发送的参数和文件,也就是用户发送来的信息将会被封装成一个FileItem对象,我们通过该对象获取请求参数或上传文件的信息。
该类不用我们手动创建,由ServletFileItem解析request后返回。
方法:
String getFieldName():获取表单项的名字,也就是input当中的name属性的值。
String getName():获取上传的文件名,普通的请求参数为null。
String getString(String encoding):获取内容,encoding参数需要指定一个字符集。
① 若为文件,将文件的流转换为字符串。
② 若为请求参数,则获取请求参数的value。
boolean isFormField():判断当前的FileItem封装的是普通请求参数,还是一个文件。
① 如果为普通参数返回:true
② 如果为文件参数返回:false
String getContentType():获取上传文件的MIME类型
long getSize():获取内容的大小
write():将文件上传到服务器
示例
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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;
import java.util.UUID;
/**
* 处理文件上传请求
* - 创建DiskFileItemFactory工厂对象【为创建ServletFileUpload对象做准备】
* - 创建ServletFileUpload对象【解析器】
* - 通过解析器的public List parseRequest(HttpServletRequest request)方法,将每个request中的数据,都解析为FileItem对象
* - 遍历List,通过FileItem中isFormField()方法,判断文件参数。【isFormField()==false】
* - 调用FileItem中的write()方法,实现文件上传
* 优化 :
* 1 优化文件名 相同
* UUID 或者 时间戳
*
*
* 2 优化限制上传文件的大小
*
*/
@WebServlet(name = "FileUploadDemoServlet",value = "/FileUploadDemoServlet")
public class FileUploadDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// response.setContentType("text/html;character=UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 创建文件上传工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
// 解析
ServletFileUpload upload = new ServletFileUpload(factory);
// 获取真实路径
String realPath = getServletContext().getRealPath("/upload");
// //判断真实路径是否存在【如不存在,创建当前路径】
File rfile = new File(realPath);
if (!rfile.exists()) {
rfile.mkdir();
}
// 设置单个文件上传大小限制
upload.setFileSizeMax(1024*120);
try {
//通过解析器将request解析为List
List<FileItem> fileItems = upload.parseRequest(request);
//遍历集合
for (FileItem fileItem : fileItems) {
//通过FileItem中isFormField()方法,判断文件参数
if (fileItem.isFormField() == false) {
// 获取文件名
String fname = fileItem.getName();
// 创建file对象
// File.separator 是转义字符的意思
// 优化一 : UUID解决重复名的问题
String uuid = UUID.randomUUID().toString().replace("-", "");
File file = new File(realPath + File.separator+ uuid + fname);
fileItem.write(file);
response.getWriter().write("文件上传成功");
} else {
// 普通参数
}
}
} catch (FileUploadBase.FileSizeLimitExceededException e) {
e.printStackTrace();
response.getWriter().write("上传文件不能超过120kb");
}catch (Exception e) {
e.printStackTrace();
}
// 判断是否是文件
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}
文件下载最直接的方法就是把文件直接放到服务器的目录中,用户直接访问该文件就可以直接下载。
但是实际上这种方式并不一定好用,比如我们在服务器上直接放置一个MP3文件,然后通过浏览器访问该文件的地址,如果是IE浏览器可能就会弹出下载窗口,而如果是FireFox和Chrome则有可能直接播放。再有就是有一些文件我们是不希望用户可以直接访问到的,这是我们就要通过Servlet来完成下载功能。
下载文件的关键是几点:
服务器以一个流的形式将文件发送给浏览器。
发送流的同时还需要设置几个响应头,来告诉浏览器下载的信息。
接下来需要以输入流的形式读入硬盘上的文件
通过response获取一个输出流,并将文件(输入流)通过该流发送给浏览器
解决中文乱码问题,在获取文件名之后为文件名进行编码
filename = java.net.URLEncoder.encode(filename,"utf-8");
示例:
import sun.misc.BASE64Encoder;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;
@WebServlet(name = "FileDownLoadDemoServlet" ,value = "/FileDownLoadDemoServlet")
public class FileDownLoadDemoServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 获取文件名参数
String fname = request.getParameter("fname");
// 获取指定文件的真实路径
String realPath = getServletContext().getRealPath("/download/" + fname);
// 创建file
File file = new File(realPath);
// 创建输入流
FileInputStream is = new FileInputStream(file);
//设置浏览器响应体文件类型
String mimeType = request.getServletContext().getMimeType(fname);
response.setContentType(mimeType);
//解决文件名中文乱码问题
String header = request.getHeader("User-Agent");
if(header != null && header.contains("Firefox")) {
fname = "=?utf-8?B?"+new BASE64Encoder().encode(fname.getBytes("utf-8"))+"?=";
}else {
fname = URLEncoder.encode(fname, "UTF-8");
}
//设置浏览器响应体内容格式,为附件格式。(告诉浏览器,文件为附件,别打开,下载。)
response.setHeader("Content-Disposition", "attachment; filename="+fname);
// 获取输出流
ServletOutputStream os = response.getOutputStream();
byte[] b = new byte[1024];
int len = 0;
while ((len = is.read()) != 0) {
os.write(b,0,len);
}
os.close();
is.close();
}
}