下载时文件名乱码和文件大小未知的问题(FireFox)

文章目录

下载文件时文件名乱码和文件大小未知

这个问题引发自标准兼容问题
FireFox 对规范要求严格, 而其他浏览器相对宽容, 所以其他浏览器会兼容, 而FireFox则不做处理
解决方案是添加 Content-Disposition 响应头

快速解决文件名乱码

传统 File 类型文件写法( java 1.4 ~ java 6 ):

File file = getFile();
String filename = java.net.URLEncoder.encode(file.getName(), "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename*=utf-8'zh_cn'" + filename);

新IO Path 类型文件写法( java 7 ~ java 13 ):

Path file = Paths.get("E:\\Downloads\\测试文件.pdf");
String filename = file.getFileName().toString();
filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.displayName());
String disposition = "attachment;filename*=utf-8'zh_cn'" + filename;
response.addHeader("Content-Disposition", disposition);

重点

重点在这一行:

 "attachment;filename*=utf-8'zh_cn'" + filename

filename*=utf-8'zh_cn' 后面直接加文件名

快速解决文件大小未知

传统IO

File file = new File("E:\\Downloads\\测试文件.pdf");
long fileSize = file .length();
response.setContentLength((int) fileSize);

新IO

Path file = Paths.get("E:\\Downloads\\测试文件.pdf");
long fileSize = Files.size(file);
response.setContentLength((int) fileSize);

重点

给 response 设置 Content-Length 头就可以解决这个问题

response.setContentLength((int) fileSize);

完整类代码

package cc.momas;

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

/** * @author Sod-Momas * @since 2020.01.16 **/
@WebServlet(value = "/download")
public class DownloadServlet extends HttpServlet { 
    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 
        // 解决乱码,一般写在过滤器 filter里
        request.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
        response.setCharacterEncoding(StandardCharsets.UTF_8.displayName());
        response.setContentType("text/html;charset=utf-8");

        // 获取文件
        Path file = Paths.get("E:\\Downloads\\测试文件.pdf");
        // 检测文件是否存在
        if (Files.notExists(file)) { 
            response.getWriter().write("指定文件不存在:" + file.toAbsolutePath());
            return;
        }

        // 获取文件名
        String filename = file.getFileName().toString();
        // 获取文件大小,单位是byte
        long fileSize = Files.size(file);

        // 将文件名URL编码
        filename = URLEncoder.encode(filename, StandardCharsets.UTF_8.displayName());
        // 添加触发下载的前缀
        // 规范参考 : Content-Disposition https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Content-Disposition
        String disposition = "attachment;filename*=utf-8'zh_cn'" + filename;

        // 添加 Content-Disposition 告诉浏览器数据展现的方式是附件下载,并且提供文件名
        response.addHeader("Content-Disposition", disposition);
        // 添加 Content-Length 告诉浏览器数据的长度,单位是 byte
        response.setContentLength((int) fileSize);
        // 添加 Content-Type 告诉浏览器这是一个二进制流
        response.setContentType("application/octet-stream");

        // 把文件从本地复制到 response 输出流输出给前端
        Files.copy(file, response.getOutputStream());
    }
}

完整项目示例

请访问我的码云仓库获取最新代码:

https://gitee.com/Sod-Momas/servlet-download-example

参考资料

博客

MDN

  • Content-Disposition Content-Disposition 响应头的作用
  • Content-Type Content-Type 实体头的作用
  • Content-Length Content-Length 是一个实体消息首部,用来指明发送给接收方的消息主体的大小,即用十进制数字表示的八位元组的数目。

RFC

    原文作者:莫弹弹
    原文地址: https://blog.csdn.net/qq_40233736/article/details/79339913
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞