文件上传和下载

文件的上传和下载是web系统中常见的功能,用于在客户端和浏览器之间传输文件,允许用户方便地共享文件、备份数据、更新软件等。


文件的上传

页面demo
<form action="/uploadFile" method="post" enctype="multipart/form-data">
    <img src="" alt="暂无数据!(这里是预览图片)" id="previewImage" width="180px" height="180px">
    <input type="file" name="uploadFile" id="uploadFile" value="上传图片" />
    <input type="submit" value="上传" />
</form>

<script> 
// 图片预览 绑定上传元素change事件
document.getElementById('uploadFile').addEventListener('change', function(event) {
        let file = event.target.files[0] // 获取选中的文件
        if (file) {
            let reader = new FileReader() // 创建FileReader对象
            reader.readAsDataURL(file)
            reader.onload = function (){
                document.getElementById('previewImage').setAttribute('src',this.result) // 设置图片的URL并展示在img元素中
            }
        }
    })
</script>
后台Servlet处理
  • 使用servlet的getPart() api来获取文件数据(注:必须在servlet3.0和tomcat8使用)
@WebServlet("/uploadFile")
@MultipartConfig   //支持part提交的方式
public class MyServlet2 extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求中的字符编码
        req.setCharacterEncoding("UTF-8");
        // 获取上传的文件
        Part uploadFile = req.getPart("uploadFile");
        String fname = uploadFile.getSubmittedFileName();
        System.out.println("上传的文件名: "+fname);
        //获取存储路径
        String realPath = req.getServletContext().getRealPath("/");
        //写入数据
        uploadFile.write(realPath+"/"+fname);
    }
}
在上面的代码中可以考虑加上一些限制因素
@MultipartConfig(fileSizeThreshold = 1024 * 1024 * 2, // 2MB(设置文件大小,超过这个大小,文件将会写入磁盘而不是内存,防止内存溢出)
                 maxFileSize = 1024 * 1024 * 10,      // 10MB(允许上传的单个文件的最大大小,超过会拒绝上传)
                 maxRequestSize = 1024 * 1024 * 50)   // 50MB(指定整个请求的最大大小,包括所有文件和表单数据。超过会拒绝上传)
  • 使用commons-fileupload库来上传文件

先添加库(或依赖)

	<dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId> <!-- 文件上传 -->
      <version>1.4</version>
    </dependency>

主要涉及的api有:

DiskFileItemFactory: 用于创建 FileItem 对象的工厂类。FileItem 对象表示上传的文件或表单字段。
ServletFileUpload: 用于解析 HTTP 请求,将请求中的文件项(FileItem)解析为文件或表单字段。它可以处理多部分请求,即包含文件上传的表单。
@WebServlet("/uploadFile")
public class MyServlet extends HttpServlet {
   
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 判断是否为文件表单(enctype="multipart/form-data")
        if (ServletFileUpload.isMultipartContent(req)) {
            DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();
            ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);
            servletFileUpload.setHeaderEncoding("utf-8"); //解决接受到的中文文件名为乱码的问题
            try {
                // 获取完整路径
                String  fileRealPath = this.getServletContext().getRealPath("/upload/");
                System.out.println("获取到的完整路径:"+fileRealPath);
                File fileUpload = new File(fileRealPath);
                if (!fileUpload.exists()) { //如果文件路径不存在就创建
                    System.out.println(fileUpload.mkdirs()?"创建文件成功":"文件创建失败!");
                }
                //获取请求里面的数据
                List<FileItem> fileItems = servletFileUpload.parseRequest(req);
                for (FileItem fileItem : fileItems) {
                    System.out.println( fileItem);
                    //判断form表单中的数据类型
                    if (fileItem.isFormField()){ //判断是文件还是文本数据(如果为true则是text)
                        String name = fileItem.getString("utf-8");
                        System.out.println("name =" +name);
                    }
                  // 获取文件名(注意:不同浏览器提交的文件名方式不一样,有的是 D:/test/t1.jpg 有点是t1.jpg)
                    String fname = fileItem.getName();
                    fname = fname.substring(fname.lastIndexOf("\\")+1); //只截取文件名
                    fileItem.write(new File(fileRealPath + fname));
                    //删除临时文件
                    fileItem.delete();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
后台Spring处理
	@PostMapping("/fileUpload")
    @ResponseBody
    public String fileUpload(@RequestParam("uploadFile") MultipartFile file, HttpServletRequest request) {
        if (!file.isEmpty()) {
            try {
                String fileName = file.getOriginalFilename();
                // 使用ServletContext获取当前Web应用的根目录
                String savePath = request.getServletContext().getRealPath("/") +"/uploads/"+ fileName;
                file.transferTo(new File(savePath)); //写入指定文件
                return "yes";
            } catch (IOException e) {
                return "error";
            }
        } else {
            return "file Empty!";
        }
    }

文件上传注意事项:

  • 在保存使用文件名时,可以加一些前缀(比如uuid、timestamp)+文件名,防止文件同名在保存时被覆盖。
  • 如果文件都上传到同一个目录下,当上传文件很多时会导致访问文件变慢,因此可以将文件放在不同目录下,比如一天上传的文件都放在一个文件夹下。
  • 一个完美的文件上传要考虑的因素有很多,比如断点续传、文件大小、尺寸大小、分片上传、防止恶意上传等,在项目部中可以考虑使用WebUploader组件
  • 在项目中需要考虑有限制的使用文件上传,如果上传太多文件会造成服务器空间被大量占用(比如微信朋友圈一次最多发9张图片等)

文件下载

<img src="upload/test.jpg" alt="暂无数据!" width="180px" height="180px">
<button onclick="location.href='/downloadFile'">下载图片到本地</button>

文件下载时,请求的响应头有:

  1. Content-Disposition:表示下载的数据展示方式,比如是内联形式(网页形式或者网页一部分),或者是文件下载方式 attachment
  2. Content-Type:指定返回数据的类型(MIME类型)
@WebServlet("/downloadFile")
public class MyServlet2 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求中的字符编码
        req.setCharacterEncoding("UTF-8");
        // 要下载的文件路径
        String downPath = "/upload/test.jpg";
        //设置请求响应头Content-Disposition Content-Type
        String mimeType = req.getServletContext().getMimeType(downPath);
        resp.setContentType(mimeType);
        //设置Content-Disposition
        // 针对不同浏览器,对下载时,显示文件名进行编码u川和base64
        if(req.getHeader("User-Agent").contains("Firefox")){
            //火狐Base64编码
            resp.setHeader("Content-Disposition","attachment;filename==?UTF-8?B?"+
                    new BASE64Encoder().encode("test.jpg".getBytes(StandardCharsets.UTF_8))+"?=");
        }else{
            //其他主流ie/chrome 使用URL编码操作
            resp.setHeader("Content-Disposition","attachment;filename="+
                    URLEncoder.encode("test.jpg","UTF-8"));
        }
        InputStream resourceAsStream = req.getServletContext().getResourceAsStream(downPath);
        //获取响应的输出流(Servlet有个Writer写入返回数据(文本),如果是二进制数据则使用OutputStream)
        OutputStream outputStream = resp.getOutputStream();
        //将输入流的数据写入输出流
        byte[] buffer = new byte[1024];
        int length;
        while ((length = resourceAsStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, length);
        }
    }
}

注意设置响应头的Content-Disposition(因为不同的浏览器有不同的编码方式)

//设置Content-Disposition
 // 针对不同浏览器,对下载时,显示文件名进行编码u川和base64
if(req.getHeader("User-Agent").contains("Firefox")){
	//火狐Base64编码
    resp.setHeader("Content-Disposition","attachment;filename==?UTF-8?B?"+
                    new BASE64Encoder().encode("test.jpg".getBytes(StandardCharsets.UTF_8))+"?=");
}else{
      //其他主流ie/chrome 使用URL编码操作
    resp.setHeader("Content-Disposition","attachment;filename="+
                    URLEncoder.encode("test.jpg","UTF-8"));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值