Java学习笔记-全栈-web开发-12-文件上传和下载

2021-08-18 15:29:37 浏览数 (1)

1. 文件上传

1.1 准备

前提

  • 提供form表单,method必须是POST
  • form表单的enctype必须是multipart/form-data
    • 若使用默认的,则只能获取到文件名
  • 提供input type="file"类的上传输入域

enctype属性

enctype作用:告知服务器请求正文的MIME类型。(请求消息头:与Content-Type作用是一致的)

可选值:

  • application/x-www-form-urlencoded(默认): 正文:username=admin&password=123 服务器获取数据:String username = request.getParameter(“username”);
  • multipart/form-data: 正文:

服务器获取数据:不能通过request.getParameter(String)方法获取指定的表单字段字符内容,因为文件上传表单已经不在是字符内容,而是字节内容,需要通过IO流获取

1.2 案例 (了解)

html

Servlet

这种办法获取到的数据会带分割串,处理比较麻烦,因此一般使用第三方包

1.3 fileupload (重点)

1.3.1 简介

fileupload是由apache的commons组件提供的上传组件。 它最主要的工作就是帮我们解析request.getInputStream()。

使用fileupload只需要2个jar包

  • commons-fileupload.jar,核心包;
  • commons-io.jar,依赖包。

fileupload的核心类有:

  • DiskFileItemFactory
  • ServletFileUpload
  • FileItem

1.3.2 解析原理

1.3.3 FileItem对象

FileItem对象对应一个表单项(表单字段)。可以是文件字段或普通字段

  • boolean isFormField():判断当前表单字段是否为普通文本字段,如果返回false,说明是文件字段;
  • String getFieldName():获取字段名称,例如:,返回的是username;
  • String getString():获取字段的内容,如果是文件字段,那么获取的是文件内容,当然上传的文件必须是文本文件;
  • String getName():获取文件字段的文件名称;(a.txt)
  • String getContentType():获取上传的文件的MIME类型,例如:text/plain。
  • int getSize():获取上传文件的大小;
  • InputStream getInputStream():获取上传文件对应的输入流;
  • void write(File):把上传的文件保存到指定文件中。
  • delete():删除临时文件

1.3.4 使用fileupload

代码语言:javascript复制
@WebServlet("/UploadServlet")
public class UploadServlet extends HttpServlet{
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException,IOException {

        //1.判断是否为文件上传表单
        if(ServletFileUpload.isMultipartContent(req)){
            //2.创建个文件工厂
            FileItemFactory factory = new DiskFileItemFactory();
            //3.创建文件上传对象
            ServletFileUpload fileUpload = new ServletFileUpload(factory);

            //4.解析请求
            try{
                List<FileItem> items = fileUpload.parseRequest(req);
                for(FileItem item : items){
                    if(item.isFormField()){//普通表单字段
                        processFormField(item);
                    }else{//文件字段
                        processFile(item);
                    }
                }
            }catch (FileUploadException fue){
                fue.printStackTrace();
            }

        }

    }

    private void processFile(FileItem item) {
        String fieldName = item.getFieldName();
        String fieldValue = item.getString();
        //1.获取文件扩展名
        String suffix = item.getName().split("\.")[1];
        String saveFileName = UUID.randomUUID().toString()   "."   suffix;
        System.out.println(saveFileName    ":"   item.getContentType());

        //2.创建文件保存的文件夹
        String folderPath = this.getServletContext().getRealPath("/WEB-INF/upload");//获取项目真实路径
        System.out.println("保存的路径:"   folderPath);
        File file = new File(folderPath);
        if(!file.exists()){
            file.mkdirs();
        }

        //3.保存文件
        //文件完整保存路径
        String filePath = folderPath   "/"   saveFileName;
        try {
            item.write(new File(filePath));
item.delete();//删除临时文件
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void processFormField(FileItem item) {
        String fieldName = item.getFieldName();
        String fieldValue = item.getString();
        System.out.println(fieldName   ":"   fieldValue);
    }
}

1.4 保存路径配置

1.4.1 添加日期目录

1.4.2 将日期转换为16进制符

上述方法源自视频教学,但是似乎有误。 可能是每个Date对象都有不同的hashCode造成的,改为:将Date赋值给String对象,使用String创建hashcode

1.5 注意事项

1.5.1 中文乱码

表单普通字段乱码用item.getString(“UTF-8”);

文件名乱码可用

1.5.2 文件上传大小限制

单个文件大小:

  • ServletFileUpload.setFileSizeMax(字节)

总文件大小:(多文件上传)

  • ServletFileUpload.setSizeMax(字节)

1.5.3 临时文件问题

DiskFileItemFactory:

  • 作用:产生FileItem对象
  • 内部有一个缓存,缓存大小默认是10Kb。如果上传的文件超过10Kb,用磁盘作为缓存。

存放缓存文件的目录在哪里? 默认是系统的临时目录。

如果自己用IO流实现的文件上传,要在流关闭后,清理临时文件。 在最后加上FileItem.delete(),删除临时文件

1.6 多文件上传

动态添加

代码语言:javascript复制
<form action="/UploadServlet"  method="post" enctype="multipart/form-data">
    用户名:<input type="text" name="username" placeholder="请输入用户名"><br>
    密码:<input type="password" name="password" placeholder="请输入密码"><br>
    <div id="div1">
        <div>
            <input type="file" name="photo" /><input type="button" value="添加" onclick="addFile()"/><br />
        </div>
    </div>
    <input type="submit" value="提交">
</form>
代码语言:javascript复制
<script type="text/javascript">
    function addFile(){
        //得到div容器
        var div1 = document.getElementById("div1");
        div1.innerHTML  =  "<div><input type='file' name='photo' /><input type='button' value='删除' οnclick='delFile(this)'/><br /></div>";
    }

    function delFile(input){
        //使用input对象的爷爷删除他的爸爸
        input.parentNode.parentNode.removeChild(input.parentNode);
    }
</script>

2. 文件下载

2.1 响应头设置

文件下载需要设置两个响应头

  • Content-Disposition attachment;filename=xx.png 以附件形式下载
  • Content-Type application/octet-stream 二进制流

2.2 案例

代码语言:javascript复制
@WebServlet("/DownloadServlet")
public class DownloadServlet extends HttpServlet{
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        //1.文件下载的位置
		//此处可通过获取客户端参数然后在数据库中查找是否存在文件
        String filePath = "C:/test.jpg";

        //2.文件名
        String fileName = "img.jpg";
        fileName = URLEncoder.encode(fileName,"UTF-8");//中文名文件需要进行URL编码
        //3.设置响应头
        resp.setHeader("Content-Disposition","attachment;filename="   fileName);
        resp.setContentType("application/octet-stream");

        //4.响应文件
        File file = new File(filePath);
        InputStream is = new FileInputStream(file);
        byte[] bytes = new byte[1024 * 8];
        int len;
        OutputStream os = resp.getOutputStream();
        while((len = is.read(bytes)) != -1){
            os.write(bytes,0,len);
        }

        //5.关闭流
        is.close();
        os.close();
    }
}

3. 总结

实质上,就是IO流的使用 由于文件在网络上通过字节流传输,因此通过字节IO流实现即可

0 人点赞