在Golang的Web开发中,通过net/http包提供的ResponseWriter接口,可以实现向客户端输出文件流完成文件下载的功能,整个过程需要正确配置响应头、读取文件内容并合理传输数据。

核心响应头配置
要实现正确的文件下载,首先需要设置合适的响应头,让浏览器识别这是文件下载请求而不是普通页面响应。常用的响应头包括以下几个:
- Content-Type:设置文件的MIME类型,不确定时可以设置为application/octet-stream,表示二进制流
- Content-Disposition:设置下载文件的名称,需要正确编码避免中文乱码
- Content-Length:设置文件的大小,浏览器可以显示下载进度
基础实现示例
下面是一个简单的文件下载实现,读取服务器本地文件并通过ResponseWriter输出:
package main
import (
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"url"
)
// 文件下载处理函数
func fileDownloadHandler(w http.ResponseWriter, r *http.Request) {
// 要下载的本地文件路径
filePath := "./test_files/demo.pdf"
// 获取文件信息
fileInfo, err := os.Stat(filePath)
if err != nil {
http.NotFound(w, r)
return
}
// 打开文件
file, err := os.Open(filePath)
if err != nil {
http.Error(w, "打开文件失败", http.StatusInternalServerError)
return
}
defer file.Close()
// 设置响应头
// 设置MIME类型为二进制流
w.Header().Set("Content-Type", "application/octet-stream")
// 处理文件名,避免中文乱码
fileName := filepath.Base(filePath)
encodedFileName := url.PathEscape(fileName)
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", encodedFileName))
// 设置文件大小
w.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
// 将文件内容复制到ResponseWriter
_, err = io.Copy(w, file)
if err != nil {
http.Error(w, "文件传输失败", http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/download", fileDownloadHandler)
fmt.Println("服务启动在 :8080 端口")
http.ListenAndServe(":8080", nil)
}
大文件下载优化
上面的基础实现适合小文件下载,如果是大文件,直接一次性读取传输会占用大量内存,可以使用分块传输的方式优化:
package main
import (
"io"
"net/http"
"os"
"path/filepath"
"url"
)
func largeFileDownloadHandler(w http.ResponseWriter, r *http.Request) {
filePath := "./test_files/large_video.mp4"
fileInfo, err := os.Stat(filePath)
if err != nil {
http.NotFound(w, r)
return
}
file, err := os.Open(filePath)
if err != nil {
http.Error(w, "打开文件失败", http.StatusInternalServerError)
return
}
defer file.Close()
// 设置响应头
w.Header().Set("Content-Type", "application/octet-stream")
fileName := filepath.Base(filePath)
encodedFileName := url.PathEscape(fileName)
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", encodedFileName))
w.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
// 设置缓冲区,分块读取传输
buffer := make([]byte, 32*1024) // 32KB缓冲区
for {
n, err := file.Read(buffer)
if n > 0 {
_, writeErr := w.Write(buffer[:n])
if writeErr != nil {
return
}
// 刷新缓冲区,确保数据及时发送
if flusher, ok := w.(http.Flusher); ok {
flusher.Flush()
}
}
if err != nil {
if err == io.EOF {
break
}
http.Error(w, "文件读取失败", http.StatusInternalServerError)
return
}
}
}
func main() {
http.HandleFunc("/download_large", largeFileDownloadHandler)
http.ListenAndServe(":8080", nil)
}
常见问题说明
文件名中文乱码
直接在Content-Disposition中设置中文文件名会出现乱码,需要使用url.PathEscape对文件名进行编码,同时采用filename*=UTF-8''编码后文件名的格式,大部分浏览器都支持这种编码方式。
文件下载不完整
如果出现下载的文件不完整,需要检查是否正确关闭了文件、是否在传输过程中中断了连接,另外要确保没有在传输完成后再次修改ResponseWriter的状态。
支持断点续传
如果需要支持断点续传,需要解析请求头中的Range字段,返回对应的文件分片,并设置响应状态码为206 Partial Content,同时设置Content-Range响应头,实现逻辑相对复杂,需要根据实际需求扩展。
GolangResponseWriter文件下载文件流输出修改时间:2026-06-30 13:00:27