根本原因是HTTP响应头Content-Disposition的filename字段仅支持ASCII,中文需按RFC 5987标准使用filename=UTF-8''xxx格式,且必须用url.PathEscape编码、双单引号间无空格,避免同时设置filename和filename。

为什么 c.Header("Content-Disposition", ...) 设置后文件名还是乱码
浏览器对中文文件名的编码处理不一致,直接拼接中文会导致 Chrome 显示为 %E6%96%87%E4%BB%B6.pdf,Safari 可能直接截断。必须按 RFC 5987 标准构造 filename*=UTF-8''... 格式。
- 用
url.PathEscape编码原始文件名(不是query.Escape或手动strings.ReplaceAll) - 固定前缀写成
filename*=UTF-8'',注意两个单引号之间**不能有空格** - 不要同时设置
filename和filename*,否则部分旧版 IE 会优先取前者导致乱码
// 正确示例:重命名为“报告_2024.pdf”
fileName := "报告_2024.pdf"
c.Header("Content-Disposition", "attachment; filename=\""+path.Base(fileName)+"\"; filename*=UTF-8''"+url.PathEscape(fileName))
c.Header("Content-Type", "application/octet-stream")
c.File("/path/to/file.pdf")
用 c.File() 还是 c.Data() 更合适
c.File() 自动处理 ETag、If-Modified-Since 等,适合静态文件;但若需动态生成、权限校验或流式传输大文件,c.Data() 更可控。
- 用
c.File()时,路径必须是服务端绝对路径,相对路径会 404 - 用
c.Data()需手动设置Content-Length,否则浏览器无法显示下载进度 - 大文件(>100MB)建议用
c.DataFromReader()避免内存拷贝,配合io.Copy流式读取
// 动态生成 CSV 并下载
c.Header("Content-Disposition", "attachment; filename=\"data.csv\"")
c.Header("Content-Type", "text/csv; charset=utf-8")
c.Data(200, "text/csv; charset=utf-8", []byte("a,b,c\n1,2,3"))
如何避免下载时触发 XSS 或路径遍历
用户传入的文件名若未经清洗,可能通过 ../ 跳出根目录,或注入 HTML/JS 到 Content-Disposition 中。
- 永远不要直接拼接用户输入到
c.File()路径中 —— 先用filepath.Clean()归一化,再检查是否在白名单目录内 - 重命名用的文件名要过滤掉控制字符、斜杠、空格、双引号等,推荐用正则
^[a-zA-Z0-9\u4e00-\u9fa5._-]+$白名单匹配 - 禁用
Content-Type: text/html下载,防止 IE 尝试渲染而非下载
Chrome 下载失败但状态码是 200 怎么查
常见原因是响应头缺失或冲突:Content-Length 与实际字节数不符、Content-Transfer-Encoding 错误、或 Nginx/CDN 缓存了错误的 Content-Type。
- 用浏览器 DevTools 的 Network → Response Headers 确认是否存在
Content-Disposition且格式合法 - 检查
Content-Type是否为application/octet-stream或明确的二进制类型,避免用text/plain - 如果走反向代理,确保 Nginx 没有重写
Content-Disposition,配置中加proxy_pass_request_headers on;


















