一、文件上传
1.准备工作
创建一个注册页面,要求:
- 上传控件所在的表单中的method是POST
- 上传文件的控件的type是file类型
- 表单的编码为multipart/form-data,此时在Servlet中不能通过getParameter()获取参数了
<span style="color: red">${error}</span>
<form action="/upload" method="post" enctype="multipart/form-data">
头像:<input type="file" name="headImg" accept="image/*"/> <br/>
昵称:<input type="text" name="username"/> <br/>
邮箱:<input type="email" name="email"/> <br/>
<input type="submit" value="注册" />
</form>
导入Apache的两个jar包,读文档写代码。
- commons-fileupload-1.2.2.jar
- commons-io-1.4.jar
方法和步骤:
- 得到请求对象(一个表单控件对应一个FileItem对象)的集合
// 创建工厂对象,工厂有创建FileItem的能力
DiskFileItemFactory factory = new DiskFileItemFactory();
// 把工厂对象传到文件上传处理器中
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析请求,也就是把请求中对象信息封装到list集合中
List<FileItem> items = upload.parseRequest(req);
- 迭代出每个FileItem对象,进行具体操作
for(FileItem item: items){
String name = item.getFieldName();//获取表单控件的name
if(item.isFormField()){//普通的表单控件
String value = item.getString("UTF-8");//获取表单中填写的值
}else{//带有上传文件的表单控件
String value = FilenameUtils.getName(item.getName());//获取上传文件的文件名称
item.write(new File("D:/",item.getName()));//把上传的文件写到本地,拷贝操作
}
}
2.细节
IE6中item.getName()获取的不是上传文件的文件名称,其中带有路径. 所以我们使用FilenameUtils.getName(item.getName());可以确定获取的是文件名称.
上传的文件一般放在应用中的文件夹里面,并且为了保证上传 相同文件名不 被覆盖采用UUID生生 随机名称, getServetContext().getRealPath("/WEB-INF/uploadFile" + UUID.randomUUID().toString() + "." +FilenameUtils.getExtension(item.getName()))));
所上传的文件大小是有限制的。缓存大小,默认为10KB,可以这样设置factory.setSizeThreshold(20 * 1024);
常用方法:
item.getFieldName()//获取表单控件的name item.getString("UTF-8");// 获取表单中填写的值,编码用utf-8 FilenameUtils.getExtension(item.getName());//获取文件的后缀名 FilenameUtils.getName(item.getName());//获取文件的名称 getServletContext().getRealPath("相对路径");//获取一个绝对路径
3.重构
抽取文件上传的工具类FileUtil,主要需要处理文件格式不对的异常,这里使用自定义异常来做.
public class FileUtil {
/**
* 对上传的文件类型的约束
*/
private static final String ALLOWED_IMAGE_TYPE = "png;jgp;gif";
protected static void upload(HttpServletRequest req){
boolean isMultipart = ServletFileUpload.isMultipartContent(req);
if (!isMultipart) {
return;
}
try {
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
/**
* 上传文件的大小约束
* 1.单个文件的大小约束
* 2.单次请求中全部的数据大小的约束
*/
upload.setFileSizeMax(1024*1024);//1M 单个文件不能超过类1M
upload.setSizeMax(1024*1024); //2M 单次请求大小不能超过2M
@SuppressWarnings("unchecked")
List<FileItem> items = upload.parseRequest(req);
for (FileItem item : items) {
String fieldName = item.getFieldName();// 获取表单控件的name
if (item.isFormField()) {// 普通的表单控件
String value = item.getString("UTF-8");// 获取表单中填写的值,编码用utf-8
System.out.println(fieldName + "--" + value);
} else {// 带有上传控件的表单
// =========================================
String ext = FilenameUtils.getExtension(item.getName());
String[] allowType = ALLOWED_IMAGE_TYPE.split(";");
if (!Arrays.asList(allowType).contains(ext)) {
throw new LogicException("请上传正确的图片格式"); //下面的LogicException 会catch住
}
// =========================================
}
}
}catch(SizeLimitExceededException e) {
throw new LogicException("改次请求文最大不能超过2M",e);//都把异常抛给了调用者也就是UploadServlet
}catch(FileSizeLimitExceededException e) {
throw new LogicException("单个文件不能超过1M",e);
}catch(LogicException e) {
throw e; //抛给调用者-->Servlet
}catch (Exception e) {
e.printStackTrace();
}
}
}
定义一个的登录异常类继承于RuntimeException
public class LogicException extends RuntimeException {
private static final long serialVersionUID = 1L;
/**
* @param message 错误信息
* @param cause 异常的根本原因
*/
public LogicException(String message, Throwable cause) {
super(message, cause);
}
public LogicException(String message) {
super(message);
}
}
在Servlet里面只需要调用FileUtil中的upload方法,并且处理异常,同时把异常共享到jsp中.
@WebServlet("/upload")
public class UpLoadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
FileUtil.upload(req);
}catch(LogicException e) { //处理异常信息
String error = e.getMessage();
req.setAttribute("error", error);
//把异常信息共享给jsp
req.getRequestDispatcher("/register.jsp").forward(req, resp);
}
}
}
二、文件下载
文件的下载只需要在jsp中的a标签href属性中的地址是本地文件就ok了,这里主要考虑的问题是中文乱码问题还有浏览器兼容的问题
先简单写一个下载的页面,为了简单直接向后台传入文件名的参数
<a href="/download?fileName=中文测试.rar">中文测试.rar</a>
<a href="/download?fileName=avatar.rar">avatar.rar</a>
@WebServlet("/download")
public class DownLoadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取被下载资源的名称
String fileName = req.getParameter("fileName");
fileName = new String(fileName.getBytes("ISO-8859-1"), "UTF-8");// GET请求--处理中文乱码
// 2.从服务器中找到被下载资源的绝对路径
String realPath = super.getServletContext().getRealPath("/WEB-INF/service/" + fileName);
System.out.println(realPath);
// 告诉浏览器不要直接打开,弹出下载框
resp.setContentType("application/x-msdownload");
String userAgent = req.getHeader("User-Agent");//得到请求头中信息来判断哪个浏览器访问
System.out.println(userAgent);
if (userAgent.contains("Mozilla")) {
//IE
fileName = URLEncoder.encode(fileName, "UTF-8");
}else {
// 非IE浏览器
fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
// 设置下载文件的建议保存名称
resp.setHeader("Content-Disposition", "attachment; filename=" + fileName);
// 3.把服务器磁盘中的文件拷贝到程序中
Files.copy(Paths.get(realPath), resp.getOutputStream());
}
}
}