基于gin框架的文件上传(逐行解析)
记录一下使用gin框架完成一个文件上传的功能,一下是实现该功能的代码,适合小白,代码都有逐行解释!
app.go
:
package router
import (
"chat/service"
"github.com/gin-gonic/gin"
)
func Router() *gin.Engine {
r := gin.Default()
//上传文件,使用post请求
r.POST("/attach/upload", service.Upload)
return r
}
attach.go
:
package service
import (
"chat/utils"
"fmt"
"github.com/gin-gonic/gin"
"io"
"math/rand"
"net/http"
"os"
"strings"
"time"
)
func Upload(c *gin.Context) {
UploadLocal(c)
}
// 上传文件到本地
func UploadLocal(c *gin.Context) {
w := c.Writer //获取响应的gin上下文写入器
req := c.Request//获取当前请求对象
//从请求中通过FormFile方方法获取指定名称("file")的文件
//srcFile为获取到的源文件
//head为文件的头部信息
//err为可能出现的错误
srcFile, head, err := req.FormFile("file")
if err != nil {
//使用utils.RespFail()函数响应写入失败信息和错误内容
utils.RespFail(w, err.Error())
}
//定义默认的文件后缀为".png"
suffix := ".png"
//从head中获取文件信息head有以下常用变量
//head.Header 文件的头部信息,包含一些与文件相关的元数据,比如文件的类型、大小
//head.Size 文件的大小
//head.Filename 文件的名字
ofilName := head.Filename
//定义一个字符串切片tem,使用strings。Split()将文件名ofilName按照(.)进行分割再装入这个切片当中,
tem := strings.Split(ofilName, ".")
//如果分割后的结果长度大于1,表示有后缀
if len(tem) > 1 {
// 将后缀更新为分割后的最后一部分(该切片的最后一个元素)
suffix = "." + tem[len(tem)-1]
}
// 根据当前时间的 Unix 时间戳、随机生成的 32 位整数和后缀生成文件名
fileName := fmt.Sprintf("%d%04d%s", time.Now().Unix(), rand.Int31(), suffix)
//使用os.Create()在指定路径("./files/"+fileName)创建新的文件
dstFile, err := os.Create("./files/" + fileName)
if err != nil {
//使用utils.RespFail()函数响应写入失败信息和错误内容
utils.RespFail(w, err.Error())
}
// 使用 io.Copy 方法将源文件的内容拷贝到目标文件中
// err 为可能出现的拷贝过程中的错误
_, err = io.Copy(dstFile, srcFile)
if err != nil {
//使用utils.RespFail()函数响应写入失败信息和错误内容
utils.RespFail(w, err.Error())
}
//构建文件的完整的URL
url := "./files/" + fileName
// 使用 utils.RespOK 函数向响应写入成功信息、文件的 URL 以及成功消息
utils.RespOK(w, url, "发送成功")
}
resp.go
:主要用处理请求响应的各种状态
package utils
import (
// 导入相关包
"encoding/json"
"fmt"
"net/http"
)
type H struct {
// 定义结构体 H,包含代码、消息、数据、行数和总数等字段
Code int
Msg string
Data interface{}
Rows interface{}
Total interface{}
}
// Resp 函数,用于响应数据
func Resp(w http.ResponseWriter, code int, data interface{}, msg string) {
// 设置响应头的内容类型为 JSON
w.Header().Set("Content-Type", "application/json")
// 设置响应状态码为 200(OK)
w.WriteHeader(http.StatusOK)
h := H{
// 初始化结构体 H 的字段
Code: code,
Data: data,
Msg: msg,
}
// 对结构体 H 进行 JSON 序列化操作
ret, err := json.Marshal(h)
if err!= nil {
// 处理序列化错误
fmt.Println(err)
}
w.Write(ret)
}
// RespList 函数,用于响应列表数据
func RespList(w http.ResponseWriter, code int, data interface{}, total interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
h := H{
// 初始化结构体 H 的字段
Code: code,
Rows: data,
Total: total,
}
ret, err := json.Marshal(h)
if err!= nil {
// 处理序列化错误
fmt.Println(err)
}
w.Write(ret)
}
// RespFail 函数,用于响应失败信息
func RespFail(w http.ResponseWriter, msg string) {
// 调用 Resp 函数,设置代码为-1,表示失败
Resp(w, -1, nil, msg)
}
// RespOK 函数,用于响应成功信息
func RespOK(w http.ResponseWriter, data interface{}, msg string) {
// 调用 Resp 函数,设置代码为 0,表示成功
Resp(w, 0, data, msg)
}
// RespOKList 函数,用于响应成功的列表信息
func RespOKList(w http.ResponseWriter, data interface{}, total int) {
// 调用 RespList 函数,设置代码为 0,表示成功
RespList(w, 0, data, total)
}
main.go
:
package main
import "chat/router"
func main() {
r := router.Router()
r.Run(":8081")
}
写完了:测试一下
使用postman提交一个文件参数
查看是否上传成功
可以看到文件上传成功了!