希曼日记

文件上传

本页目录 3
  1. 单文件
  2. 上传文件至Minio
  3. 参考

单文件

  1. 通过POSTform/data 方法选择文件
  2. golang获取POST/PUT方法的FormFile(key) (file,err)获取到文件
  3. 通过SaveUploadFile保存文件 SaveUploadFile(file, path) err
  4. file 文件, 通过POST/PUT方法上传的, 通过content.FormFIle()接收
  5. path 保存的路径与文件名
userGroup.POST("/upload", func(context *gin.Context) {
			file, _ := context.FormFile("file")
			err := context.SaveUploadedFile(file, "./upload/"+file.Filename)
			if err != nil {
				panic(err)
			}
			fmt.Println(file.Filename)
			context.JSON(http.StatusOK, gin.H{
				"msg": "OK",
			})
		})

前后端上传示例: 前端

const upload = (e: any) => {
	console.log(e.target.files)
	const formData = new FormData()
	formData.append('file', e.target.files[0], e.target.files[0].name)
	fetch('http://localhost:4000/upload', {
		method: 'POST',
		body: formData,
	})
	.then((res) => {
		if (!res.ok){
			throw new Error('上传失败')
		}
		return res.json()
	})
	.then(res => {
		console.log(res.json())
	})
	.catch(err => {
		console.error(err)
	})
}

export default function Home () {

	return (
		<main>

			<label>
				<input hidden accept='image/*' type='file'
							 onChange={ (event: ChangeEvent<HTMLInputElement>) => upload(event) } />
			</label>
			
		</main>
	)
}

Golang

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

func main() {
	server := gin.Default()
	server.Use(cors())
	server.POST("/upload", func(context *gin.Context) {
		file, _ := context.FormFile("file")
		err := context.SaveUploadedFile(file, "./upload/1.jpg")
		if err != nil {
			panic(err)
		}
		fmt.Println(file.Filename)
		context.JSON(http.StatusOK, gin.H{
			"msg": "OK",
		})
	})

	err := server.Run(":4000")
	if err != nil {
		return
	}
}

func cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.Request.Header.Get("Origin") //请求头部
		if origin != "" {
			//接收客户端发送的origin (重要!)
			c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
			//服务器支持的所有跨域请求的方法
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
			//允许跨域设置可以返回其他子段,可以自定义字段
			c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session")
			// 允许浏览器(客户端)可以解析的头部 (重要)
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
			//设置缓存时间
			c.Header("Access-Control-Max-Age", "172800")
			//允许客户端传递校验信息比如 cookie (重要)
			c.Header("Access-Control-Allow-Credentials", "true")
			//
			//c.Header("Content-Type", "application/json")
		}

		//允许类型校验
		if method == "OPTIONS" {
			c.Header("Access-Control-Allow-Origin", "*")
			c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization") //自定义 Header
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT")
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
			c.Header("Access-Control-Allow-Credentials", "true")
			c.AbortWithStatus(http.StatusNoContent)
		}

		defer func() {
			if err := recover(); err != nil {
				log.Printf("Panic info is: %v", err)
			}
		}()
		c.Next()
	}
}

上传文件至Minio

package service

import (
	"github.com/gin-gonic/gin"
	"im/api/oss"
	"im/utils"
	"log"
	"net/http"
	"os"
	"path/filepath"
)

func Upload(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		c.String(http.StatusBadRequest, "get form err: %s", err.Error())
		log.Fatalln("存储文件失败" + err.Error())
	}

	dst := "./uploads/"
	if err := c.SaveUploadedFile(file, dst+file.Filename); err != nil {
		log.Fatalln("存储文件失败" + err.Error())
	}

	// 上传文件至minio
	bucketName := os.Getenv("MINIO_BUCKET")                   // Minio存储桶名
	uploadDir, _ := utils.GetPath("/uploads/", file.Filename) // 待上传的文件路径
	uploadDirToLinux := filepath.FromSlash(uploadDir)         // 转成Minio的服务器路径, 本机部署在Linux,通过FromSlash替换成/路径
	uploadInfo, uploadErr := oss.UploadFile(bucketName, file.Filename, uploadDirToLinux)
	if uploadErr != nil {
		log.Fatalln("上传文件失败:", err)
	}

	if err != nil {
		c.AbortWithStatusJSON(http.StatusOK, gin.H{
			"message": "上传文件至文件存储服务器失败:" + err.Error(),
			"body":    nil,
			"code":    http.StatusInternalServerError,
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"message": "上传文件至文件存储服务器成功",
		"body":    uploadInfo,
		"code":    http.StatusOK,
	})
}

func UploadFile(bucketName, filename, filepath string) (minio.UploadInfo, error) {
	// 初使化minio client对象。
	MinioClient, err := minio.New(os.Getenv("MINIO_URL"), &minio.Options{
		Creds:  credentials.NewStaticV4(os.Getenv("MINIO_ACCESS_KEY"), os.Getenv("MINIO_SECRET_KEY"), ""),
		Secure: false,
	})
	if err != nil {
		log.Fatalln("初始化Minio客户端失败" + err.Error())
	}
	uploadInfo, uploadErr := MinioClient.FPutObject(
		context.TODO(),
		bucketName,
		filename,
		filepath,
		minio.PutObjectOptions{
			ContentType: "application/zip",
		})
	if uploadErr != nil {
		log.Fatalln("上传文件失败:", err)
	}
	return uploadInfo, nil
}

参考

  1. MDN
  2. 掘金