gin 圖片上傳到本地或者oss


路由層


func registerCommonRouter(v1 *gin.RouterGroup) {

	up := v1.Group("upload")
	{
		up.POST("/upLoadFile", commons.FileUpLoad) //單文件上傳
		up.POST("/upLoadMultipartFile", commons.FileUpLoadMultipart) //單文件上傳
	}
}

控制器


//單文件上傳
/**
@ 單文件上傳
@ name="file" 文件名
@ multipart/form-data;
*/
func FileUpLoad(c *gin.Context) {
	err,host, pathRelative := tools.UplaodFile(c)
	if err != nil {
		app.Error(c, 400, err, "")
		return
	}
	app.OK(c, gin.H{
		"host": host,
		"uri":  pathRelative,
		"url":  path.Join(host,pathRelative),
	}, "success")
}


/**
@ 多文件上傳
@ name="files[]" 文件名
@ multipart/form-data;
*/
func FileUpLoadMultipart(c *gin.Context) {
	resFileInfo ,err:= tools.UpLoadMultipartFile(c)
	if err != nil {
		app.Error(c, 400, err, "")
		return
	}
	app.OK(c, resFileInfo, "ok")
}

tools里

package tools

import (
	"errors"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/gin-gonic/gin"
	"mime/multipart"
	"os"
	"path"
	"reflect"
	"strings"
	"time"
)

// @ author haima
type UploadConf struct {
	UpdateVilidateExtString string           `json:"updateVilidateExtString"` //從配置里獲取,空為不限制  示例 "jpg,jpeg,png,gif,bmp"
	FileMaxSize             int64            `json:"fileMaxSize"`             //單位Mb 0:不限制大小 / 5
	Drive                   string           `json:"drive"`
	UploadConfLocal         *UploadConfLocal `json:"uploadConfLocal"`
	UploadConfOss           *UploadConfOss   `json:"uploadConfOss"`
	UploadConfQiniu         *UploadConfQiniu `json:"uploadConfQiniu"`
}

// 本地配置
type UploadConfLocal struct {
	Host string `json:"updateVilidateExtString"` //根域名
}

// oss配置
// 阿里雲主賬號AccessKey擁有所有API的訪問權限,風險很高。強烈建議您創建並使用RAM賬號進行API訪問或日常運維,請登錄 https://ram.console.aliyun.com 創建RAM賬號。
type UploadConfOss struct {
	Host            string `json:"host"`     //根域名
	Endpoint        string `json:"endpoint"` //根域名
	AccessKeyId     string `json:"accessKeyId"`
	AccessKeySecret string `json:"accessKeySecret"`
	BucketName      string `json:"bucketName"`
}

type UploadConfQiniu struct {
	AccessKey string `json:"accessKey"`
	SecretKey string `json:"secretKey"`
	Bucket    string `json:"bucket"`
	ImgPath   string `json:"imgPath"`
}

func InitUploadConf() *UploadConf {
	return &UploadConf{
		UpdateVilidateExtString: "",    //從配置里獲取,空為不限制  示例 "jpg,jpeg,png,gif,bmp"
		FileMaxSize:             5,     //單位Mb 0:不限制大小 / 5
		Drive:                   "local", // local / oss / qiniu
		UploadConfLocal: &UploadConfLocal{
			Host: "http://localhost:8001", //根域名
		},
		//以下oss賬號僅限測試。非法操作者。請自重 感謝 壹cup清茶 貢獻的測試賬號
		UploadConfOss: &UploadConfOss{
			//公司的賬號
			Endpoint:        "http://oss-cn-beijing.aliyuncs.com",            //阿里雲請求地址 定值,寫死
			Host:            "https://test.oss-cn-beijing.aliyuncs.com", //返回的自己的阿里雲域名
			AccessKeyId:     "LTAI4FbzKpo9UcoQeXWqVabc",
			AccessKeySecret: "n6xiRIUZhn6dZsnsXYFtA2cZ9jFabc",
			BucketName:      "test",
		},
		//請自行七牛申請對應的 公鑰 私鑰 bucket 和 域名地址
		UploadConfQiniu: &UploadConfQiniu{
			AccessKey: "",
			SecretKey: "",
			Bucket:    "",
			ImgPath:   "",
		},
	}
}

// 初使化配置
var UploadConfig = new(UploadConf)

func init() {
	UploadConfig = InitUploadConf()
}

//單個文件 上傳文件到本地目錄里
func UplaodFile(c *gin.Context) (err error, host, pathRelative string) {
	// 單個文件
	file, err := c.FormFile("file")
	if err != nil {
		err = errors.New(fmt.Sprintf("獲取數據失敗,%v", err))
		return
	}
	return actionUplaodFile(c, file)
}

//多圖片返回參
type ResFiles struct {
	HostListStr string     `json:"hostListStr"`
	UriListStr  string     `json:"uriListStr"`
	UrlListStr  string     `json:"urlListStr"`
	FileList    []FileItem `json:"fileList"`
}
type FileItem struct {
	Host string `json:"host"`
	Uri  string `json:"uri"`
	Url  string `json:"url"`
}

//多文件上傳
func UpLoadMultipartFile(c *gin.Context) (resFiles ResFiles,err error) {
	form, _ := c.MultipartForm()
	files := form.File["files[]"]
	if len(files) < 1{
		err = errors.New("文件不能為空")
		return
	}
	for _, file := range files {
		err, host, pathRelative := actionUplaodFile(c, file)
		if err != nil {
			continue
		}
		var rItem FileItem
		rItem.Host = host
		rItem.Uri = pathRelative
		rItem.Url = path.Join(host , pathRelative)
		resFiles.FileList = append(resFiles.FileList, rItem)
		if resFiles.HostListStr == "" {
			resFiles.HostListStr = rItem.Host
			resFiles.UriListStr = rItem.Uri
			resFiles.UrlListStr = rItem.Url
		} else {
			resFiles.HostListStr += fmt.Sprintf(",%s", rItem.Host)
			resFiles.UriListStr += fmt.Sprintf(",%s", rItem.Uri)
			resFiles.UrlListStr += fmt.Sprintf(",%s", rItem.Url)
		}
	}
	return resFiles ,nil
}

/**
@ 執行單個文件 上傳文件到本地目錄里
@ 調用方法 上傳圖片流,文件名為file   調用tools.UplaodFileToLocal(c)
@ return host 根域名
@ return path 上傳后的相對文件路徑
@ author haima
*/
func actionUplaodFile(c *gin.Context, file *multipart.FileHeader) (err error, host, pathRelative string) {

	// 判斷上傳文件的大小
	if UploadConfig.FileMaxSize != 0 {
		fsize := file.Size //上傳文件的大小
		if fsize > UploadConfig.FileMaxSize*1024*1024 {
			err = errors.New(fmt.Sprintf("只能上傳小於 %dMb 的文件 ", UploadConfig.FileMaxSize))

			return
		}
	}

	//獲取上傳文件后綴
	extString := strings.ToUpper(Ext(file.Filename))
	if extString == "" {
		err = errors.New(fmt.Sprintf("上傳失敗,文件類型不支持,只能上傳 %s 格式的。", UploadConfig.UpdateVilidateExtString))
		return
	}

	//驗證文件類型
	if len(UploadConfig.UpdateVilidateExtString) > 0 {
		VilidateExtStrSliceTmp := strings.Split(strings.ToUpper(UploadConfig.UpdateVilidateExtString), ",")
		VilidateExtStrSlice := make([]string, 0)
		for _, v := range VilidateExtStrSliceTmp {
			VilidateExtStrSlice = append(VilidateExtStrSlice, fmt.Sprintf(".%s", v))
		}

		//驗證文件類型
		//extString .JPG
		//VilidateExtStrSlice  []string{".JPG",".JPEG",".PNG",".GIF",".BMP"}
		if !ContainArray(extString, VilidateExtStrSlice) {
			err = errors.New(fmt.Sprintf("上傳失敗,文件類型不支持,只能上傳 %s 格式的。", UploadConfig.UpdateVilidateExtString))
			return
		}
	}
	//上傳的文件名
	file.Filename = fmt.Sprintf("%s%s", time.Now().Format("20060102150405"), file.Filename) // 文件名格式 自己可以改 建議保證唯一性 20060102150405test.xlsx
	switch UploadConfig.Drive {
	case "local":
		return uploadfileToLocal(c, file, file.Filename)
	case "oss":
		return uploadfileToOss(c, file, file.Filename)
	case "qiniu":
		return nil, UploadConfig.UploadConfQiniu.ImgPath, "qiniu 開發中...."
	default:
		err = errors.New("只支持上傳到本地,oss,七牛")
		return
	}
}

func uploadfileToLocal(c *gin.Context, file *multipart.FileHeader, filename string) (err error, host, pathRelative string) {
	host = UploadConfig.UploadConfLocal.Host
	filepath := path.Join("static/uploadfile",time.Now().Format("20060102"))
	//上傳到的路徑
	pathRelative = path.Join(filepath, filename) //路徑+文件名上傳
	//如果沒有filepath文件目錄就創建一個
	if _, err := os.Stat(filepath); err != nil {
		if !os.IsExist(err) {
			os.MkdirAll(filepath, os.ModePerm)
		}
	}
	// 上傳文件到指定的目錄
	err = c.SaveUploadedFile(file, pathRelative)
	if err != nil {
		err = errors.New(fmt.Sprintf("上傳失敗,%v", err))
		return
	}
	return
}

func uploadfileToOss(c *gin.Context, file *multipart.FileHeader, filename string) (err error, host, ossPathFileName string) {
	host = UploadConfig.UploadConfOss.Host
	err, _, localPathFileName := uploadfileToLocal(c, file, filename)
	if err != nil {
		err = errors.New(fmt.Sprintf("上傳失敗,%v", err))
	}

	ossPath := path.Join("upload" ,time.Now().Format("20060102"))
	ossPathFileName = path.Join(ossPath , file.Filename)
	// 創建OSSClient實例。
	client, err := oss.New(UploadConfig.UploadConfOss.Endpoint, UploadConfig.UploadConfOss.AccessKeyId, UploadConfig.UploadConfOss.AccessKeySecret)
	if err != nil {
		os.Remove(localPathFileName)
		err = errors.New(fmt.Sprintf("文件上傳服務器失敗. err:%s", err.Error()))
		return
	}
	// 獲取存儲空間。
	bucket, err := client.Bucket(UploadConfig.UploadConfOss.BucketName)
	if err != nil {
		os.Remove(localPathFileName)
		err = errors.New(fmt.Sprintf("文件上傳雲端失敗. err:%s", err.Error()))
		return
	}
	// 上傳文件。
	err = bucket.PutObjectFromFile(ossPathFileName, localPathFileName)
	if err != nil {
		os.Remove(localPathFileName)
		err = errors.New(fmt.Sprintf("文件上傳雲端失敗. err:%s", err.Error()))
		return
	}
	err = os.Remove(localPathFileName)  //如果本地不想刪除,可以注釋了
	if err != nil {
		fmt.Println(err)
	}
	return
}

//獲取文件的擴展名
func Ext(path string) string {
	for i := len(path) - 1; i >= 0 && path[i] != '/'; i-- {
		if path[i] == '.' {
			return path[i:]
		}
	}
	return ""
}

//Contain 判斷obj是否在target中,target支持的類型array,slice,map   false:不在 true:在
func ContainArray(obj interface{}, target interface{}) bool {
	targetValue := reflect.ValueOf(target)
	switch reflect.TypeOf(target).Kind() {
	case reflect.Slice, reflect.Array:
		for i := 0; i < targetValue.Len(); i++ {
			if targetValue.Index(i).Interface() == obj {
				return true
			}
		}
	case reflect.Map:
		if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
			return true
		}
	}

	return false
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM