在 Web 开发中,文件上传 是常见需求,例如头像上传、文档存储、图片分享等功能。Go 语言的标准库
net/http
已经内置了对multipart/form-data
类型的支持,能让我们轻松构建一个文件上传服务。
本文将带你实现一个可运行的文件上传接口,并附带 HTML 表单和 curl
测试方法。
一、目标功能
-
路径:
/upload
-
方法:
POST
-
表单字段:
file
:上传文件desc
:文件描述(可选)
-
保存文件到本地
./uploads
目录 -
返回 JSON 结果
二、核心知识点
r.ParseMultipartForm(maxMemory)
:解析multipart/form-data
表单r.FormFile("file")
:获取上传的文件io.Copy(dst, src)
:保存文件到本地- 表单字段获取:
r.FormValue("desc")
- 文件权限控制:
os.Create()
/os.MkdirAll()
三、完整代码
go
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
)
type UploadResponse struct {
Filename string `json:"filename"`
Size int64 `json:"size"`
Desc string `json:"desc"`
Status string `json:"status"`
}
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
// 解析上传表单(maxMemory 5MB,超过部分存临时文件)
err := r.ParseMultipartForm(5 << 20)
if err != nil {
http.Error(w, "Error parsing form: "+err.Error(), http.StatusBadRequest)
return
}
// 获取表单字段
desc := r.FormValue("desc")
// 获取文件
file, handler, err := r.FormFile("file")
if err != nil {
http.Error(w, "Error retrieving file: "+err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
// 确保保存目录存在
os.MkdirAll("./uploads", os.ModePerm)
// 保存文件
filePath := filepath.Join("uploads", handler.Filename)
dst, err := os.Create(filePath)
if err != nil {
http.Error(w, "Error saving file: "+err.Error(), http.StatusInternalServerError)
return
}
defer dst.Close()
size, err := io.Copy(dst, file)
if err != nil {
http.Error(w, "Error writing file: "+err.Error(), http.StatusInternalServerError)
return
}
// 返回 JSON 响应
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(UploadResponse{
Filename: handler.Filename,
Size: size,
Desc: desc,
Status: "success",
})
}
func main() {
http.HandleFunc("/upload", uploadHandler)
fmt.Println("文件上传服务已启动:http://localhost:8080/upload")
http.ListenAndServe(":8080", nil)
}
四、测试方法
1. HTML 表单测试
保存为 upload.html
:
html
<!DOCTYPE html>
<html>
<body>
<h2>文件上传测试</h2>
<form action="http://localhost:8080/upload" method="post" enctype="multipart/form-data">
文件描述: <input type="text" name="desc"><br><br>
选择文件: <input type="file" name="file"><br><br>
<input type="submit" value="上传">
</form>
</body>
</html>
打开浏览器选择文件并提交。
2. curl 命令测试
bash
curl -X POST http://localhost:8080/upload \
-F "desc=测试图片" \
-F "file=@test.png"
五、运行效果
成功上传后返回:
json
{
"filename": "test.png",
"size": 15324,
"desc": "测试图片",
"status": "success"
}
文件会保存在 ./uploads/test.png
。
六、注意事项
-
上传限制 : 通过
r.ParseMultipartForm(maxMemory)
控制内存占用,超过部分会写入临时文件。 -
安全性:
- 校验文件类型(避免执行恶意文件)
- 生成唯一文件名(避免覆盖)
- 设置合理的文件大小限制(可用
http.MaxBytesReader
)
-
跨域请求 : 如果前端与后端不在同一域名,需要设置
Access-Control-Allow-Origin
等 CORS 头。
七、进阶扩展
- 上传时自动生成文件唯一 ID(防止文件名冲突)
- 返回文件访问 URL
- 将文件上传到云存储(如 AWS S3、阿里云 OSS)
- 支持多文件同时上传(
r.MultipartForm.File["file"]
)