在Golang中实现HTTP文件下载,核心是通过net/http包处理HTTP请求,结合io包完成文件流的读取和传输,同时需要正确设置响应头告知客户端文件的相关信息。

基础实现步骤
实现HTTP文件下载的基本流程分为三步:首先解析请求获取要下载的文件路径,然后打开目标文件并获取文件信息,最后将文件内容写入HTTP响应流。下面是基础的实现代码:
package main
import (
"net/http"
"os"
"path/filepath"
)
func downloadHandler(w http.ResponseWriter, r *http.Request) {
// 获取请求中的文件名参数
fileName := r.URL.Query().Get("file")
if fileName == "" {
http.Error(w, "缺少文件参数", http.StatusBadRequest)
return
}
// 拼接文件绝对路径,避免路径遍历漏洞
baseDir := "./uploads"
filePath := filepath.Join(baseDir, filepath.Base(fileName))
// 打开文件
file, err := os.Open(filePath)
if err != nil {
http.Error(w, "文件不存在", http.StatusNotFound)
return
}
defer file.Close()
// 获取文件信息
fileInfo, err := file.Stat()
if err != nil {
http.Error(w, "获取文件信息失败", http.StatusInternalServerError)
return
}
// 设置响应头
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename=""+fileName+""")
w.Header().Set("Content-Length", string(fileInfo.Size()))
// 将文件内容写入响应
http.ServeContent(w, r, fileName, fileInfo.ModTime(), file)
}
func main() {
http.HandleFunc("/download", downloadHandler)
http.ListenAndServe(":8080", nil)
}
关键响应头说明
正确设置响应头是保证文件能正常下载的关键,常用的响应头作用如下:
- Content-Type:设置为application/octet-stream表示二进制流,浏览器会触发下载行为,而不是尝试解析内容
- Content-Disposition:attachment表示附件形式下载,filename参数指定客户端保存的默认文件名
- Content-Length:告知客户端文件的总大小,方便客户端显示下载进度
大文件下载优化
对于大文件下载,直接使用http.ServeContent已经做了分块传输的优化,它会自动处理Range请求,支持断点续传。如果需要手动实现分块读取,可以使用io.Copy结合缓冲区,避免一次性将大文件加载到内存:
func bigFileDownloadHandler(w http.ResponseWriter, r *http.Request) {
fileName := r.URL.Query().Get("file")
if fileName == "" {
http.Error(w, "缺少文件参数", http.StatusBadRequest)
return
}
baseDir := "./uploads"
filePath := filepath.Join(baseDir, filepath.Base(fileName))
file, err := os.Open(filePath)
if err != nil {
http.Error(w, "文件不存在", http.StatusNotFound)
return
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
http.Error(w, "获取文件信息失败", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename=""+fileName+""")
w.Header().Set("Content-Length", string(fileInfo.Size()))
// 使用io.Copy分块传输,默认缓冲区大小32KB
_, err = io.Copy(w, file)
if err != nil {
// 处理传输过程中的错误,比如客户端断开连接
return
}
}
常见问题处理
路径遍历漏洞防护
如果直接使用用户传入的文件名拼接路径,攻击者可能传入../../etc/passwd这类路径访问系统敏感文件,必须使用filepath.Base获取纯文件名,或者限制文件只能从指定目录读取。
断点续传支持
上面的http.ServeContent已经内置支持Range请求,当客户端发送Range头时,会自动返回对应范围的内容,响应状态码为206 Partial Content,不需要额外编写逻辑。
错误场景处理
需要覆盖的常见错误场景包括:文件不存在返回404、无权限访问返回403、请求参数错误返回400,同时可以在响应中返回友好的错误提示信息,方便客户端排查问题。