学静思语
Published on 2025-02-15 / 0 Visits
0
0

文件上传和下载

文件上传和下载

一、文件的上传

1.代码

<%--
  Created by IntelliJ IDEA.
  User: 韩顺平
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- 指定了base标签 -->
    <base href="<%=request.getContextPath()+"/"%>">
    <style type="text/css">
        input[type="submit"] {
            outline: none;
            border-radius: 5px;
            cursor: pointer;
            background-color: #31B0D5;
            border: none;
            width: 70px;
            height: 35px;
            font-size: 20px;
        }

        img {
            border-radius: 50%;
        }

        form {
            position: relative;
            width: 200px;
            height: 200px;
        }

        input[type="file"] {
            position: absolute;
            left: 0;
            top: 0;
            height: 200px;
            opacity: 0;
            cursor: pointer;
        }
    </style>

    <script type="text/javascript">
        function prev(event) {
            //获取展示图片的区域
            var img = document.getElementById("prevView");
            //获取文件对象
            var file = event.files[0];
            //获取文件阅读器: Js的一个类,直接使用即可
            var reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = function () {
                //给img的src设置图片url
                img.setAttribute("src", this.result);
            }
        }
    </script>

</head>
<body>
<!-- 表单的enctype属性要设置为multipart/form-data
    enctype="multipart/form-data" 表示提交的数据是多个部分构造,有文件和文本
 -->

<form action="upload" method="post" enctype="multipart/form-data">
    家居图: <img src="2.jpg" alt="" width="200" height="200" id="prevView">
<%--    小伙伴愿意完成自己测试--%>
    <input type="file" name="pic" id="" value="" onchange="prev(this)"/>
<%--    <input type="file" name="pic" id="" value="提交文件" onchange="prev(this)">--%>
    家居名: <input type="text" name="name"><br/>

    <input type="submit" value="上传"/>
</form>
</body>
</html>
```txt
package com.leon.servlet;

import com.leon.util.UploadTool;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * ClassName:Servlet
 * Package:com.leon.servlet
 * Description:
 *
 * @Author: leon-->ZGJ
 * @Create: 2023/11/19 22:31
 * @Version: 1.0
 */

public class FileUploadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//        System.out.println("FileUploadServlet 被调用了~~~~");
//        System.out.println(request.getContentType());

//        UploadTool.upload(request,response);
         /*
            1. 通过ServletFileUpload类中的静态方法isMultipartContent,
               来判断Content-Type是否是multipart/form-data,也可以理解为表单中的
               enctype属性是否等于multipart/form-data
            2.底层是这样判断的: contentType.toLowerCase().startsWith("multipart/");
              返回的是boolean值

         */
        if (ServletFileUpload.isMultipartContent(request)) {
            /*
               1.因为ServletFileUpload类的构造器是public ServletFileUpload(FileItemFactory fileItemFactory){}
                 需要一个FileItemFactory接口,而DiskFileItemFactory实现了FileItemFactory接口,所以需要DiskFileItemFactory
                 用于构建一个解析上传数据的工具,也就是ServletFileUpload类
            */
            DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
//          创建一个解析上传数据的工具
//          也可以直接与上面的进行合并:new ServletFileUpload(new DiskFileItemFactory());
            ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
//          设置编码
            servletFileUpload.setHeaderEncoding("utf-8");
            try {
                /*
                  1.这个servletFileUpload.parseRequest(request)方法是把
                    表单提交的数据封装到FIleItem中
                  2.这个List使用的泛型对象是 FileItem,为什么使用这个对象呢?
                  3.因为底层返回的这个List中存储的对象就是FileItem
                    源码:
                      List items = new ArrayList();
                      FileItem fileItem;
                      for(; iter.hasNext(); items.add(fileItem)) {....}
                */
                List<FileItem> list = servletFileUpload.parseRequest(request);
//              如果不了解对象是什么结构有这几个方法[1.输出该对象2.debug]
//              这样底层的东西就会展现出来
//              循环遍历
                /*
                   name表示你的文件名
                   name=3.png,
                   StoreLocation 表示上传的文件临时存放的地方,包括临时的文件名
                   StoreLocation=C:\software\javaTool\tomcat\apache-tomcat-9.0.82\temp\ upload_46ad27a5_18bec217fa1__7fd7_00000002.tmp,
                   size表示文件的大小
                   size=76442bytes,
                   isFormField表示是否是文本
                   isFormField=false,
                   FieldName表示该表单的name属性值 <input type="file" name="pic" id="" value="" onchange="prev(this)"/>
                   FieldName=pic
                   -------------------------------------------------------
                   name表示文件名,因为是一个文本,不是一个文件,没有文件名所以等于null
                   name=null,
//                 StoreLocation表示该数据临时存放的地方包括临时的文件名
                   StoreLocation=C:\software\javaTool\tomcat\apache-tomcat-9.0.82\temp\ upload_46ad27a5_18bec217fa1__7fd7_00000003.tmp,
                   size表示数据的大小
                   size=6bytes,
                   isFormField是否是文本
                   isFormField=true,
                   FieldName表示该表单的name属性值 <input type="text" name="name">
                   FieldName=name
                */
                for (FileItem fileItem : list) {
//                System.out.println(fileItem);
//                进行处理吗,判断是否是一个文件
                    if (fileItem.isFormField()) {
                        /*
                        不是文件,获取它的input标签的value值
                        getName()因为这个是获取name的,也就是获取文件名,因为它没有文件名,所以值为空
                        String name = fileItem.getName();
                        System.out.println(name);
                        getString()这个是获取input标签的value值,但是还需要设置编码格式
                        */
                        String fieldName = fileItem.getString("utf-8");
                        System.out.println(fieldName);
                    } else {
//                        是文件,获取文件名
                        String name = fileItem.getName();
//                        System.out.println("文件名:" +name);
                       /*
                            1.创建一个目录,这个目录在实际的工程目录下,存放上传的文件
                            2.创建一个目录名
                        */
                        String filePath = "/upload/";
                        /*
                            1.获取一个完整的路径
                            2.getRealPath("/")获取真实的工作路径
                            3.getRealPath(filePath)获取真实的工作路径然后把filePath拼接在真实的工作路径后面
                        */

                        String filRealPath = request.getServletContext().getRealPath(filePath);
//                        System.out.println(filRealPath);
//                        创建该目录
                        File fileRealPathDirectory = new File(filRealPath+ UploadTool.getDateDirectory());
                        /*
                         System.out.println(fileRealPathDirectory);
                            1.直接输出这个File类是一个路径
                            2.因为File类的toString()方法是输出该文件的路径
                        */
//                        判断该目录是否存在,如果不存在则创建
                        if(!fileRealPathDirectory.exists()){
//                            判断是否创建成功,mkdirs表示创建多级目录
                            if(fileRealPathDirectory.mkdirs()){
                                System.out.println("创建成功");
                            }else {
                                System.out.println("创建失败");
                            }
                        }
//                        将文件拷贝到创建的目录中
                        /*
                            1.为了防止文件名相同而导致的覆盖,使用UUID来解决,随机生成一个UUID作为文件名的一部分
                            为了防止加了UUID也重名再加上一个时间戳
                            2.用"_"间隔开来方便日后要获取该文件的真正文件名时,更方便
                        */
                        name = UUID.randomUUID() +"_" + System.currentTimeMillis() + "_" + name ;
                        String fileFullPath = fileRealPathDirectory +"/"+ name;
                        fileItem.write(new File(fileFullPath));

//                       响应上传成功
//                       设置Context-Type
                        response.setContentType("text/html;charset=utf-8");
                        response.getWriter().write("上传成功~~~");

                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            System.out.println("不是~~~");
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

2.注意事项

  • 如果将文件都上传到一个目录下,当上传文件很多时,会造成访问文件速度变慢,因此可以将文件上传到不同目录比如一天上传的文件,统一放到一个文件夹

  • 一个完美的文件上传,要考虑的因素很多,比如断点续传、控制图片的大小,尺寸,分片上传,防止恶意上传等,在项目中,可以考虑使用WebUploader组件(百度开发的)

    http://fex.baidu.com/webuploader/doc/index.html

  • 文件上传功能,在项目中建议有限制的使用,一般用头像、证明、合同、产品展示等,如果不加限制,会造成服务器空间被大量占用[比如b站评论,就不能传图片,微信发一次朋友圈最多9张图片等….]

  • 如果你在编写程序时,在web目录下创建一个目录时,如果目录中没有文件,在Tomcat启动时,不会在实际的工作目录下创建你在web目录下创建的目录,因为Tomcat不会在实际的工作目录中创建空目录,所以在你要创建的目录下随意的添加一个文件即可,然后再build一下后再重启或重新发布

二、文件下载

1.代码

<%--
  Created by IntelliJ IDEA.
  User: 韩顺平
  Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
    <base href="<%=request.getContextPath()+"/"%>">
</head>
<body>
<h1>文件下载</h1>
<a href="download?name=1.png">点击下载1图片</a><br/><br/>
<a href="download?name=2.png">点击下载2图片</a><br/><br/>
<a href="download?name=韩顺平java基础.pdf">韩顺平java基础.pdf</a><br/><br/>
</body>
</html>
```txt
package com.leon.servlet;

import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Encoder;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;

/**
 * ClassName:Servlet
 * Package:com.leon.servlet
 * Description:
 *
 * @Author: leon-->ZGJ
 * @Create: 2023/11/21 20:49
 * @Version: 1.0
 */

public class FileDownLoadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//        System.out.println("FileDownLoadServlet 被调用了");
//        设置请求编码
        request.setCharacterEncoding("utf-8");
//        获取要下载的文件名
        String fileName = request.getParameter("name");
//        System.out.println(name);
        /*
            1. 通过ServletContent类来获取文件的MIME类型
            2. 通过获取到的MIME类型然后再设置Content-Type的类型
        */
        ServletContext servletContext = request.getServletContext();
//        创建一个目录,也就是你存放文件的那个目录
        String downLoadPath = "/download/";
//       创建一个完整的目录,就是将获取到的文件名与你创建的存放目录进行拼接
        String downLoadFileFullPath = downLoadPath + fileName;
//        通过你给出的路径,然后去找该文件,再获取文件的MIME类型,这个是从web工程根目录开始计算的
        String mimeType = servletContext.getMimeType(downLoadFileFullPath);
//        System.out.println(mimeType);
//        设置Content-Type
        response.setContentType(mimeType);

        /*
           1.给你http响应,设置响应头 Content-Disposition
           2.这里有很多细节,比如不同的浏览器写法不一样,要考虑编码
           3.火狐 是文件名中文需要base64编码,而ie/chrome是URL编码
           4.Content-Disposition 是指定下载的数据的展示形式,如果attachment 则使用文件下载方式
        */
        if(request.getHeader("User-Agent").contains("Firefox")){
//            Base64编码
            response.setHeader("Content-Disposition","attachment;filename==?UTF-8?B?"+
                    new BASE64Encoder().encode(fileName.getBytes("UTF-8"))+"?=");
        }else {
            response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(fileName,"UTF-8"));
        }

//        通过ServletContext,创建一个字节输入流
        InputStream resourceAsStream = servletContext.getResourceAsStream(downLoadFileFullPath);
//        创建一个字节输出流
        ServletOutputStream outputStream = response.getOutputStream();
//        使用工具将输入流的数据拷贝到输出流中
        IOUtils.copy(resourceAsStream,outputStream);


    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request,response);
    }
}

2.注意事项

  • 文件下载中,文件名中文处理,在不同的浏览器需要不同的编码方式,如火狐需要base64,而ie/chrom需要URL
  • 对于网站的文件,很多文件使用另存为即可下载,对于大文件(文档、视频),会使用专业的下载工具(迅雷,百度,腾讯,华为网盘等)
  • 对于不同的浏览器,在把文件下载完毕后,处理的方式不一样,有些是直接打开文件,有些是直接将文件下载到本地/下载目录

Comment