不能直接返回文件流给前端预览,因为浏览器仅原生支持text/plain、image/*、application/pdf等mime类型内嵌渲染;而.docx、.xls等二进制格式需服务端转pdf或html后才能预览。

为什么不能直接返回文件流给前端预览
浏览器对不同 MIME 类型的处理差异很大:text/plain、image/*、application/pdf 通常能直接渲染,但 application/vnd.openxmlformats-officedocument.wordprocessingml.document(.docx)或 application/vnd.ms-excel(.xls)这类二进制文档,绝大多数浏览器不支持原生渲染,会触发下载而非预览。
所以「在线预览」本质不是“让浏览器打开”,而是“把文件转成浏览器能渲染的格式”。常见做法是服务端转 PDF 或 HTML,再交给前端展示。
- PDF 是最稳妥的中间格式:兼容性好、排版稳定、几乎所有文档都能转
- Office 文档建议用
libreoffice --headless转 PDF(轻量、开源、无依赖) - 纯文本/Markdown 可直接读取 + 设置
Content-Type: text/plain; charset=utf-8,前端用<pre class="brush:php;toolbar:false;"></pre>或代码高亮库渲染 - 图片和 PDF 文件可直接透传,但必须显式设置正确的
Content-Type和Content-Disposition: inline
用 Go 启动一个带转换能力的预览接口
核心逻辑:根据文件扩展名分流处理,调用外部工具或内置逻辑生成响应内容。
示例接口路径:/preview?file=report.docx,后端解析参数 → 读取原始文件 → 判断类型 → 转换或透传 → 返回响应。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 用
http.ServeFile直接返回静态资源只适用于已存在 PDF/图片等,不满足“任意上传文件实时预览”需求 - 关键点在于:转换过程必须加超时控制,避免 LibreOffice 卡死导致整个 HTTP handler 阻塞
- 推荐用
exec.CommandContext启动子进程,并设置context.WithTimeout - 临时文件要清理:转 PDF 后立即读取并
os.Remove,否则磁盘会被撑爆 - 不要在生产环境用
os/exec频繁启动 LibreOffice;应考虑池化或独立转换服务
cmd := exec.CommandContext(ctx, "libreoffice", "--headless", "--convert-to", "pdf", "--outdir", tmpDir, srcPath) err := cmd.Run()
如何安全地处理用户传入的文件名参数
URL 中的 file=xxx 极易引发路径遍历攻击,比如 file=../../etc/passwd。Golang 的 filepath.Clean 不足以防御,必须白名单校验。
- 禁止使用
os.Open(req.URL.Query().Get("file"))这类直读方式 - 只允许特定目录下的文件:先提取文件名(不含路径),再拼接可信根目录,最后用
filepath.Join+strings.HasPrefix校验最终路径是否仍在白名单内 - 扩展名必须严格匹配白名单:
.pdf、.jpg、.docx、.xlsx等,拒绝.sh、.py、.exe - 对上传文件本身也要做 MIME 类型校验(用
net/http.DetectContentType读前 512 字节),不能只信扩展名
PDF 流式响应与 Content-Disposition 的坑
想让浏览器内嵌显示 PDF,除了 Content-Type: application/pdf,还必须设置 Content-Disposition: inline; filename="xxx.pdf"。漏掉 inline 会导致强制下载。
- 不要用
Content-Disposition: attachment—— 这是下载语义 - 如果 PDF 来自内存(如
bytes.Buffer),直接w.Write(pdfBytes)即可;若来自文件,用http.ServeContent更安全(支持 range 请求、自动设置 etag) - 注意 Nginx / Caddy 等反向代理可能覆盖或删除
Content-Disposition头,需显式配置proxy_pass_request_headers on - 移动端 Safari 对 inline PDF 支持不稳定,建议 fallback 到
<iframe src="/preview/xxx.pdf"></iframe>再加 JS 错误捕获
真正麻烦的是 Office 转 PDF 的失败场景:LibreOffice 出错时 stdout 为空、stderr 有乱码、返回码非 0 —— 这些都要捕获并返回友好错误页,而不是让前端卡住。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










