文件的上传和下载

一、文件上传

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());
        }
    }
}
posted @ 2019-01-14 11:05 earth腾飞 阅读(...) 评论(...) 编辑 收藏