Safari浏览器下载文件时,文件名会URL encoded

问题:相同链接下载文件,safari文件名编码异常

解决:response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + URLEncoder.encode(filename, "UTF-8"));

问题描述

  • 谷歌下载(正常)

  • Safari下载(异常)

问题代码示例

@RestController
@RequestMapping("/file")
public class FileController {

    @GetMapping("/download")
    public void download(HttpServletResponse response) {
        String filename = "图片.jpg";
        String path = "img/" + filename;
        ClassPathResource classPathResource = new ClassPathResource(path);

        try (FileInputStream fis = new FileInputStream(classPathResource.getFile());
             ServletOutputStream sos = response.getOutputStream()) {
            //设置响应头
        	response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));

            IOUtils.copy(fis, sos);
        } catch (Exception e) {
            throw new RuntimeException("下载失败!");
        }
    }
}

问题解决如下

@RestController
@RequestMapping("/file")
public class FileController {

    @GetMapping("/download")
    public void download(HttpServletResponse response) {
        String filename = "图片.jpg";
        String path = "img/" + filename; // resources下路径,比如文件位置在:resources/img/图片.jpg
        ClassPathResource classPathResource = new ClassPathResource(path);

        try (FileInputStream fis = new FileInputStream(classPathResource.getFile());
             ServletOutputStream sos = response.getOutputStream()) {
            //设置响应头
            response.setHeader("Content-Disposition", "attachment;filename*=utf-8''"
                    + URLEncoder.encode(filename, "UTF-8"));

            IOUtils.copy(fis, sos);
        } catch (Exception e) {
            throw new RuntimeException("下载失败!");
        }
    }
}

关键代码

response.setHeader("Content-Disposition", "attachment;filename*=utf-8''"
                    + URLEncoder.encode(filename, "UTF-8"));

下载后文件名正常

文章参考

Safari浏览器下载文件时,文件名会URL encoded

<think>好的,我需要帮助用户解决在Java中设置HTTP响应头出现的编码错误。用户的问题是在设置Content-Type为application/pdf后,设置了字符编码为UTF-8,然后设置Content-Disposition头触发了IllegalArgumentException,具体错误是关于Unicode字符超出允许范围。 首先,我需要理解这个错误的原因。根据之前的对话,用户已经知道当使用单字节编码(如ISO-8859-1),超出0-255范围的字符会引发错误。但这里用户设置的是UTF-8,为什么还会出现这个错误呢? 接下来,用户的具体代码是: 1. response.setContentType("application/pdf"); 2. response.setContentType("UTF-8"); 3. response.setHeader("Content-Disposition", "attachment; filename=\"电子版.pdf\""); 可能的错误点在于设置Content-Disposition头文件名包含中文字符“电子版”,而HTTP头部的编码处理可能存在问题。根据RFC标准,HTTP头部的值应该使用ASCII字符,非ASCII字符需要进行编码,通常采用RFC 5987规定的编码方式,即使用filename*=UTF-8''加上URL编码后的文件名。 用户可能在设置字符编码为UTF-8,误以为这会自动处理HTTP头部的编码,但实际上,setCharacterEncoding主要影响响应体的编码,而非头部。头部需要手动处理非ASCII字符。 解决方案应该是修改Content-Disposition头,使用filename*参数并正确编码文件名。同,可能需要移除对setCharacterEncoding的调用,因为对于application/pdf内容类型,字符编码可能不适用,或者确保正确设置。 此外,用户可能在旧版浏览器中需要同提供filename和filename*参数以兼容,但现代浏览器支持filename*。 需要验证用户是否正确地进行了URL编码,并正确格式化了Content-Disposition头。例如,正确的格式应该是: Content-Disposition: attachment; filename="encoded_name.pdf"; filename*=UTF-8''%E7%94%B5%E5%AD%90%E7%89%88.pdf 最后,确保用户使用的Servlet容器版本支持RFC 5987,并且正确设置所有相关头部,避免混合使用不同编码方式导致的问题。</think>#### 问题根源分析 此错误由HTTP响应头的`filename="电子版.pdf"`中的中文字符引发,具体原因如下: 1. **HTTP头部编码限制** HTTP协议规定头部必须使用`ASCII`字符(RFC 2616),直接使用中文等非ASCII字符会触发编码错误 2. **错误配置顺序** `response.setCharacterEncoding("UTF-8")`仅影响响应体(Body)编码,对头部(Header)无效 #### 分步解决方案 ##### 方案1:RFC 5987编码(推荐) ```java String fileName = "电子版.pdf"; String encodedName = URLEncoder.encode(fileName, StandardCharsets.UTF_8) .replace("+", "%20"); // 空格特殊处理 response.setHeader("Content-Disposition", "attachment; filename=\"dummy.pdf\"; filename*=UTF-8''" + encodedName); ``` ##### 方案2:Base64编码(旧浏览器兼容) ```java String fileName = "电子版.pdf"; String encodedName = Base64.getEncoder() .encodeToString(fileName.getBytes(StandardCharsets.UTF_8)); response.setHeader("Content-Disposition", "attachment; filename=\"=?UTF-8?B?" + encodedName + "?=.pdf\""); ``` ##### 方案3:纯ASCII替代 ```java response.setHeader("Content-Disposition", "attachment; filename=\"electronic_version.pdf\""); ``` #### 必须注意的细节 1. **删除冗余编码设置** PDF二进制文件不需要字符编码声明: ```java response.setContentType("application/pdf"); // 移除 response.setCharacterEncoding("UTF-8") ``` 2. **Servlet容器要求** - Tomcat 8.0+ 默认支持RFC 5987 - 旧版本需添加配置: ```xml <Connector URIEncoding="UTF-8" useBodyEncodingForURI="true"/> ``` 3. **浏览器兼容性测试** | 编码方式 | Chrome | Firefox | IE11+ | Safari | |---------------|--------|---------|-------|--------| | RFC 5987 | ✅ | ✅ | ✅ | ✅ | | Base64 | ✅ | ✅ | ✅ | ✅ | | 直接中文 | ❌ | ❌ | ❌ | ❌ | #### 完整正确示例 ```java // 设置PDF内容类型(无需字符编码) response.setContentType("application/pdf"); // 生成RFC 5987兼容的文件名 String fileName = "电子版.pdf"; String encoded = URLEncoder.encode(fileName, StandardCharsets.UTF_8) .replace("+", "%20"); // 设置双重文件名(兼容新旧浏览器) response.setHeader("Content-Disposition", "attachment; filename=\"fallback.pdf\"; filename*=UTF-8''" + encoded); // 写入PDF二进制流 try (OutputStream os = response.getOutputStream()) { Files.copy(Paths.get("/path/to/file.pdf"), os); } ``` > 通过RFC 5987编码方案,既可以保持中文文件名可读性,又能完美兼容现代浏览器,同避免触发字符编码越界错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值