解决文件下载中文文件名乱码的最佳实践

1.方案一

如果下载的文件名包含中文,有时浏览器可能无法正确显示文件名,而会显示乱码或者一串数字和字母。这个问题可以通过设置Content-Disposition响应头的filename参数来解决,该参数可以指定要下载的文件名,并且支持URL编码。

下面是一个示例代码,演示了如何在Java Web中设置响应的文件名,以支持中文文件名的下载:

@GET
@Path("/download")
public Response downloadFile(@QueryParam("filename") String filename) throws UnsupportedEncodingException {
    // 获取要下载的文件
    File file = new File("/path/to/files/" + filename);
    
    // 创建响应
    ResponseBuilder responseBuilder = Response.ok(file);
    
    // 设置文件名
    String encodedFilename = new String(filename.getBytes("UTF-8"), "ISO-8859-1");
    responseBuilder.header("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\"");
    
    return responseBuilder.build();
}

在上述示例中,downloadFile方法使用@GET@Path注解来定义一个RESTful Web服务,该服务接收一个filename参数,表示要下载的文件名。该方法首先获取要下载的文件,然后创建一个响应,将文件作为响应的内容。

接着,将文件名进行URL编码。这里使用了filename.getBytes("UTF-8")方法将原始文件名转换为UTF-8编码的字节数组,然后使用new String构造方法将字节数组转换为ISO-8859-1编码的字符串。由于ISO-8859-1编码是浏览器默认的编码方式,因此使用这种方式可以确保浏览器能够正确显示文件名。

最后,使用responseBuilder.header方法设置Content-Disposition响应头,将编码后的文件名作为filename参数传入。最终,使用responseBuilder.build方法构建响应并返回。

2.方案二

如果文件名仍然出现乱码问题,可能是因为服务器端编码和浏览器端编码不一致导致的。你可以尝试如下方法解决:

  1. 将文件名进行URL编码后再返回给前端。可以使用java.net.URLEncoder.encode()方法进行编码,例如:
String encodedFilename = URLEncoder.encode(filename, "UTF-8");
responseBuilder.header("Content-Disposition", "attachment; filename=\"" + encodedFilename + "\"");
  1. 在页面头部设置meta标签,指定编码方式为UTF-8,例如:
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  1. 尝试使用其他浏览器访问,查看是否仍然出现乱码问题。

如果上述方法仍然无效,可能是文件名编码本身存在问题,可以考虑对文件名进行重命名,在重命名前将文件名进行URL编码,确保文件名编码正确。

前端解码
在 JavaScript 中,可以使用 decodeURIComponent() 函数对 URL 进行解码。该函数用于将 URL 中的编码的字符解码为它们原来的值。

下面是一个示例代码,演示如何使用 decodeURIComponent() 函数解码 URL:

let encodedUrl = "http%3A%2F%2Fexample.com%2Fpath%3Fquery%3D%E4%B8%AD%E6%96%87";
let decodedUrl = decodeURIComponent(encodedUrl);
console.log(decodedUrl);
// Output: http://example.com/path?query=中文

在上述示例代码中,使用了一个编码后的 URL,它包含了一些特殊字符和中文字符。然后,使用 decodeURIComponent() 函数对 URL 进行解码,并将解码后的 URL 打印出来。

需要注意的是,如果 URL 中包含的编码字符是不正确的,或者不完整的,那么 decodeURIComponent() 函数可能会抛出异常。此时需要确保 URL 编码的正确性。

如果要对整个 URL 进行解码,可以使用 decodeURI() 函数,它可以解码整个 URL,包括协议、主机名、路径和查询字符串等。例如:

let encodedUrl = "http%3A%2F%2Fexample.com%2Fpath%3Fquery%3D%E4%B8%AD%E6%96%87";
let decodedUrl = decodeURI(encodedUrl);
console.log(decodedUrl);
// Output: http://example.com/path?query=中文

使用 decodeURI() 函数可以方便地对整个 URL 进行解码,适用于需要还原整个 URL 的场景。

3.最佳实践

3.1后端java代码

@GetMapping("/download")
    public void download(HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode("网址备份_"+JWTUtils.getUserIdByHeader()+".xlsx", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName );
        List data =new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            URLModel urlModel=new URLModel();
            urlModel.setUrl("url"+i);
            urlModel.setName("setName"+i);
            urlModel.setWebCategoryName("setWebCategoryName"+i);
            data.add(urlModel);
        }

       

        EasyExcel.write(response.getOutputStream(), URLModel.class).sheet("模板").doWrite(data );
    }

3.2前端react样例

 const handleDownload = () => {
        const fileUrl = '/api/download';
        const requestOptions = {
            method: 'GET',
            headers: {
                authorization: getAuthority(),
            }
        };
        fetch(fileUrl, requestOptions)
            .then(response => {
                let filename = response.headers.get('content-disposition').split('filename=')[1];
                filename = decodeURIComponent(filename);
                response.blob().then(blob => {
                    const url = window.URL.createObjectURL(new Blob([blob]));
                    const link = document.createElement('a');
                    link.href = url;
                    link.setAttribute('download', filename);
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                });
            });

    }

你可能感兴趣的:(springboot,java,servlet,前端)