文件上传和下载

上传文件的准备工作

1.表单中的提交方式必须为POST(POST没有大小限制):<form action="#" method="post">

2.在表单中需要添加一个上传控件:<input type="file" name="headImg" />

3.必须将表单中enctype修改为multipart/form-data,数据以二进制的形式进行传递:<form action="#" method="post" enctype="multipart/form-data">

注意:此时在Servlet中就不能再使用String name = req.getParameter("name");获取请求参数

文件上传操作,实际上就是在解析数据。通常,这种操作是必须要做的,但是又没有技术含量,所有通常都应该有第三方的工具使用,如:基于Apache FileUpload组件

上传表单:

<form action="/upload" method="post" enctype="multipart/form-data" >
    账号:<input name="name"/>
    <br/>
    头像:<input type="file" name="headImg">
    <input type="submit" value="注册"/>
</form>

基于Apache FileUpload组件

操作步骤:使用Apache FileUpload上传组件实现上传功能
1.导入jar包:commons-fileupload-1.2.2.jar commons-io-1.4.jar
2.参考commons-fileupload-1.2.2siteusing.html文件,找到对应的实例代码,复制到项目中运行,观察效果

上传文件的相关问题

文件名处理:

问题:不同的用户上传的文件名称可能相同,那么后面的文件会将前面的文件覆盖掉
解决方案:使用UUID生成一个随机的字符串来作为文件名称

问题2:上传文件的路径不同写死,因为文件的路径在不同的服务器上路径不一样
解决方案:通常需要将上传文件存放到项目中的一个指定的目录中

缓存大小和临时目录

缓存大小: 指定文件超过多大的时候,将文件保存到临时目录,反之,就是讲文件保存到内存中,默认为10kb
临时目录 : 用来保存上传的临时文件,默认在tomcat/temp文件中

解决的问题:文件过大,造成内存溢出

文件类型约束

通常需要约束用户上传的文件的类型,
实现的方式:
    1.通过文件名称的后缀来判断(jpg/jpeg/png/gif)
    2.根据文件的MIME类型来判断(image/jpeg)

文件大小约束:

单个文件:upload.setFileSizeMax(50 * 1024);
整个请求:upload.setSizeMax(140 * 1024);
处理的方式和处理文件类型的方式一样

贴上上传文件的代码,方便查看:

JSP:

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>朕要注冊</title>
</head>
<body>

    <span style="color: red">
        ${errorMsg}
    </span>
    
    <form action="/upload" method="post" enctype="multipart/form-data" >
        账号:<input name="name"/>
        <br/>
        头像:<input type="file" name="headImg">
        <br/>
        头像2:<input type="file" name="headImg">
        <br/>
        <input type="submit" value="注册"/>
    </form>

</body>
</html>

三个JAVA文件:
自定义的异常类:LogicException.java

package ee.coding._01_upload;

public class LogicException extends RuntimeException{

    private static final long serialVersionUID = 1L;

    public LogicException() {
        super();
    }
    public LogicException(String message) {
        super(message);
    }

    public LogicException(String message, Throwable cause) {
        super(message, cause);
    }
}

抽成工具的上传类:FileUploadUtils.java

package ee.coding._01_upload;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase.FileSizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;

public class FileUploadUtils {
    public static void upload(HttpServletRequest req) {
        // 检查是否有文件上传请求
        boolean isMultipart = ServletFileUpload.isMultipartContent(req);
        // 如果不符合条件则结束方法,(上传要求form表单的属性enctype="multipart/form-data)
        if (!isMultipart) {
            return;
        }

        try {
            // 当需要调用子类对象的方法时不能使用父类型来接收对象声明
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // 设置缓存大小
            factory.setSizeThreshold(1024 * 20);

            // 创建一个文件上传对象
            ServletFileUpload upload = new ServletFileUpload(factory);

            // 设置单个文件的大小
            upload.setFileSizeMax(1024 * 242);
            // 设置整个请求的文件大大小限制
            upload.setSizeMax(1024 * 245);

            // 解析请求
            // 解析请求中的数据,每个表单元素<input/>解析为一个FileItem对象
            @SuppressWarnings("unchecked")
            List<FileItem> items = upload.parseRequest(req);
            for (FileItem item : items) {

                if (item.isFormField()) {
                    // 普通的表单元素<input name="name">
                    String fileName = item.getFieldName();
                    System.out.println(fileName);
                    System.out.println(item.getString("UTF-8"));
                } else {
                    // 获取上传文件的MIME类型
                    String contentType = item.getContentType();
                    if (!contentType.startsWith("image/")) {
                        throw new LogicException("亲,您提交的文件类型不正确!");
                    }
                    // 上传控件<input type="file" name="headImg">
                    System.out.println(item.getContentType());
                    System.out.println(item.getSize());
                    System.out.println(item.getName());
                    // 将上传文件保存到服务器的某个文件夹中
                    try {
                        // 使用uuid解决文件名重复问题
                        String uuidString = UUID.randomUUID().toString();
                        String extensionName = FilenameUtils.getExtension(item.getName());
                        String realfileName = uuidString + "." + extensionName;
                        // 获取指定文件夹的绝对路径
                        String realPath = req.getServletContext().getRealPath("/upload");
                        item.write(new File(realPath, realfileName));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } catch (FileSizeLimitExceededException e) {
            throw new LogicException("您上传的文件大小超过限制", e);
        } catch (SizeLimitExceededException e) {
            throw new LogicException("您上传的总文件大小超过限制", e);
        } catch (LogicException e) {
            throw e;
        } catch (FileUploadException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}

Servlet:UploadServlet.java

package ee.coding._01_upload;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            //调用工具类的上传方法
            FileUploadUtils.upload(req);
        } catch (LogicException e) {
            // 将错误信息添加到请求作用域中
            req.setAttribute("errorMsg", e.getMessage());
            // 通过请求转发跳回到上传页面
            req.getRequestDispatcher("/rg.jsp").forward(req, resp);
        }
    }
}

文件的下载:这个比较简单,直接贴代码吧

JSP:

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>下载资源</title>
</head>
<body>
    <h1>资源下载</h1>
    <a href="/download?filename=1.jpg">1.jpg</a>
    <br>
    <a href="/download?filename=2.jpg">2.jpg</a>
    <br>
    <a href="/download?filename=ssr.zip">ssr.zip</a>
</body>
</html>

JAVA文件:

package ee.coding._02_download;

import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Paths;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/download")
public class DownloadServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        //获取到用户要下载的文件名称
        String filename = req.getParameter("filename");
        //帮用户找到相应的文件
        //根据相对路径找到绝对路径
        String realPath = req.getServletContext().getRealPath("WEB-INF/download");
        File file = new File(realPath,filename);
        //设置响应报头:解决文件名称问题
        String header = req.getHeader("User-Agent");
        //解决中文名称的文件问题
        if(header.contains("MSIE")){
            //IE浏览器
            resp.setHeader("Content-Disposition", "attachment;filename="
                    + URLEncoder.encode(filename,"utf-8"));
        }else{
            //非IE,先解码再编码
            resp.setHeader("Content-Disposition", "attachment;filename="
                    + new String(filename.getBytes("UTF-8"),"ISO8859-1") );
        }
        //将找到的文件响应给浏览器
        Files.copy(Paths.get(file.getAbsolutePath()),resp.getOutputStream());
    }
}

项目源码:FileUpload-FileDownload.zip