在Web开发中,文件上传和下载是非常常见的功能需求,Golang凭借标准库的丰富能力,可以很方便地实现这两个功能,不需要依赖复杂的第三方框架。下面我们将分别讲解文件上传和下载的具体实现方式。

Golang实现文件上传
文件上传的核心逻辑是解析客户端发送的multipart/form-data格式请求,从中提取上传的文件内容,保存到服务器指定路径。首先需要确保HTTP请求的方法为POST,并且请求头包含正确的Content-Type。
上传功能实现步骤
- 设置路由,限定只处理POST请求
- 调用
r.ParseMultipartForm方法解析表单数据,设置内存缓存大小 - 通过
r.FormFile获取上传的文件句柄和文件头信息 - 创建本地文件,将上传的文件内容拷贝到本地文件
- 返回上传结果给客户端
完整上传代码示例
package main
import (
"fmt"
"io"
"net/http"
"os"
)
// 上传文件保存目录
const uploadDir = "./uploads"
func uploadHandler(w http.ResponseWriter, r *http.Request) {
// 只处理POST请求
if r.Method != http.MethodPost {
http.Error(w, "只支持POST请求", http.StatusMethodNotAllowed)
return
}
// 解析multipart表单,内存缓存10MB
err := r.ParseMultipartForm(10 << 20)
if err != nil {
http.Error(w, "解析表单失败: "+err.Error(), http.StatusBadRequest)
return
}
// 获取上传的文件
file, header, err := r.FormFile("uploadFile")
if err != nil {
http.Error(w, "获取上传文件失败: "+err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
// 创建保存目录(如果不存在)
if _, err := os.Stat(uploadDir); os.IsNotExist(err) {
os.MkdirAll(uploadDir, 0755)
}
// 创建本地文件
localPath := uploadDir + "/" + header.Filename
localFile, err := os.Create(localPath)
if err != nil {
http.Error(w, "创建本地文件失败: "+err.Error(), http.StatusInternalServerError)
return
}
defer localFile.Close()
// 拷贝文件内容
_, err = io.Copy(localFile, file)
if err != nil {
http.Error(w, "保存文件失败: "+err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "文件上传成功,保存路径: %s", localPath)
}
func main() {
http.HandleFunc("/upload", uploadHandler)
fmt.Println("服务启动在 :8080")
http.ListenAndServe(":8080", nil)
}Golang实现文件下载
文件下载的核心逻辑是读取服务器本地的文件内容,设置正确的HTTP响应头,告诉浏览器这是一个需要下载的文件,然后将文件内容写入响应体即可。
下载功能实现步骤
- 获取客户端请求的文件名,校验文件是否存在
- 设置响应头Content-Type为application/octet-stream,指定Content-Disposition为附件下载
- 打开本地文件,读取内容写入响应流
- 处理文件读取和写入过程中的错误
完整下载代码示例
package main
import (
"fmt"
"net/http"
"os"
"path/filepath"
)
// 文件存储根目录,和上传目录保持一致
const fileDir = "./uploads"
func downloadHandler(w http.ResponseWriter, r *http.Request) {
// 获取请求的文件名
fileName := r.URL.Query().Get("filename")
if fileName == "" {
http.Error(w, "缺少filename参数", http.StatusBadRequest)
return
}
// 拼接文件路径,避免路径遍历攻击
filePath := filepath.Join(fileDir, 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", fmt.Sprintf("attachment; filename=\"%s\"", fileName))
w.Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
// 将文件内容拷贝到响应流
_, err = io.Copy(w, file)
if err != nil {
http.Error(w, "文件下载失败", http.StatusInternalServerError)
return
}
}
func main() {
http.HandleFunc("/download", downloadHandler)
fmt.Println("服务启动在 :8080")
http.ListenAndServe(":8080", nil)
}注意事项
在实际生产环境中,还需要注意以下几点问题:
- 文件上传需要限制文件大小和类型,避免恶意文件上传占用服务器资源
- 文件名需要处理特殊字符,避免路径遍历攻击,建议使用UUID重命名文件
- 下载大文件时可以考虑使用分片传输,提升传输效率
- 文件操作需要做好权限控制,避免未授权访问敏感文件
以上就是Golang实现文件上传下载的完整方法和示例代码,开发者可以根据自己的业务需求对代码进行调整和扩展。