613 字
3 分钟
文件处理

上传单个文件#

r.POST("/upload", func(c *gin.Context) {
// 获取上传文件
file, err := c.FormFile("file")
if err != nil {
c.JSON(400, gin.H{
"error": err.Error(),
})
return
}
// 创建上传目录
err := os.MkdirAll("./upload", os.ModePerm)
if err != nil {
panic(err)
}
// 自定义文件名
filename := fmt.Sprintf( "%d%s", time.Now().UnixNano(), filepath.Ext(file.Filename), )
// 将上传文件保存到指定路径
err = c.SaveUploadedFile(file, "./upload/"+file.Filename)
if err != nil {
c.JSON(500, gin.H{
"error": err.Error(),
})
return
}
c.JSON(200, gin.H{
"filename": file.Filename,
})
})

上传多个文件#

r.POST("/uploads", func(c *gin.Context) {
// 一次上传多个文件
form, err := c.MultipartForm()
if err != nil {
c.JSON(400, gin.H{
"error": err.Error(),
})
return
}
files := form.File["files"]
for _, file := range files {
c.SaveUploadedFile(file, "./upload/"+file.Filename)
}
c.JSON(200, gin.H{
"count": len(files),
})
})

获取上传文件信息#

r.POST("/info", func(c *gin.Context) {
// 获取文件名、大小、请求头等信息
file, err := c.FormFile("file")
if err != nil {
return
}
// 获取上传文件后缀
ext := filepath.Ext(file.Filename)
fmt.Println(ext)
c.JSON(200, gin.H{
"filename": file.Filename,
"size": file.Size,
"header": file.Header,
})
})

读取上传文件内容#

r.POST("/read", func(c *gin.Context) {
// 读取文件内容,而不直接保存
fileHeader, _ := c.FormFile("file")
file, _ := fileHeader.Open()
defer file.Close()
data, _ := io.ReadAll(file)
c.JSON(200, gin.H{
"content": string(data),
})
})

判断文件类型#

r.POST("/judgeType", func(c *gin.Context) {
fileHeader, _ := c.FormFile("file")
file, _ := fileHeader.Open()
defer file.Close()
buffer := make([]byte, 512)
file.Read(buffer)
// 检测文件真实 MIME 类型
contentType := http.DetectContentType(buffer)
fmt.Println(contentType)
})

限制上传大小#

r := gin.Default()
// 最大 8MB
r.MaxMultipartMemory = 8 << 20
// 或者业务判断
r.POST("/judgeSize", func(c *gin.Context) {
file, _ := c.FormFile("file")
if file.Size > 5*1024*1024 {
c.JSON(400, gin.H{
"error": "文件超过5MB",
})
return
}
})

下载本地文件#

r.GET("/download", func(c *gin.Context) {
// 返回服务器本地文件
c.File("./upload/test.pdf")
})

强制下载文件#

r.GET("/download", func(c *gin.Context) {
// 浏览器弹出下载,而不是预览
c.FileAttachment(
"./upload/test.pdf",
"test.pdf",
)
})

返回字节流#

r.GET("/data", func(c *gin.Context) {
// 返回内存中的二进制数据
data := []byte("Hello Gin")
c.Data(
200,
"text/plain",
data,
)
})

下载 JSON 文件#

r.GET("/json", func(c *gin.Context) {
// 动态生成 JSON 文件下载
user := map[string]any{
"name": "Tom",
"age": 18,
}
data, _ := json.Marshal(user)
c.Header(
"Content-Disposition",
"attachment; filename=user.json",
)
c.Data(
200,
"application/json",
data,
)
})

自定义响应 Header 下载#

r.GET("/download", func(c *gin.Context) {
c.Header(
"Content-Disposition",
"attachment; filename=test.pdf",
)
c.File("./upload/test.pdf")
})

流式下载大文件#

r.GET("/stream", func(c *gin.Context) {
// 避免一次性读入内存,适合视频、压缩包等大文件
file, err := os.Open("./movie.mp4")
if err != nil {
c.Status(404)
return
}
defer file.Close()
io.Copy(c.Writer, file)
})

暴露静态资源目录#

r := gin.Default()
// 将整个目录作为静态资源目录
r.Static("/static", "./static")

暴露单个静态文件#

r := gin.Default()
// 只暴露一个文件
r.StaticFile(
"/favicon.ico",
"./favicon.ico",
)

返回 HTML 文件#

r := gin.Default()
// 直接返回指定 HTML 页面
r.StaticFile(
"/index",
"./index.html",
)
文件处理
https://shirone.mysqil.com/posts/gin/文件处理/
作者
HikariTish
发布于
2026-07-18
许可协议
CC BY-NC-SA 4.0

分享文章

生成精美分享图或复制链接,与更多人分享本文。

继续阅读

沿着主题读

基于共同的标签与分类

换条路线

从其他文章中稳定抽取