美文网首页青春志石臼墙话板河南科技学院
基于Go语言实现OSS文件上传(local)

基于Go语言实现OSS文件上传(local)

作者: 胡小毛 | 来源:发表于2022-04-29 22:56 被阅读0次

    本文件上传接口适用于gin-vue-admin框架
    https://www.gin-vue-admin.com/

    Config:

    server/config/oss.go

    package config
    
    type Local struct {
        Path string `mapstructure:"path" json:"path" yaml:"path" `
    }
    
    type Qiniu struct {
        Zone          string `mapstructure:"zone" json:"zone" yaml:"zone"`
        Bucket        string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
        ImgPath       string `mapstructure:"img-path" json:"imgPath" yaml:"img-path"`
        UseHTTPS      bool   `mapstructure:"use-https" json:"useHttps" yaml:"use-https"`
        AccessKey     string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"`
        SecretKey     string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"`
        UseCdnDomains bool   `mapstructure:"use-cdn-domains" json:"useCdnDomains" yaml:"use-cdn-domains"`
    }
    
    type Minio struct {
        Id       string `mapstructure:"id" json:"id" yaml:"id"`
        Path     string `mapstructure:"path" json:"path" yaml:"path"`
        Token    string `mapstructure:"token" json:"token" yaml:"token"`
        Bucket   string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
        UseSsl   bool   `mapstructure:"use-ssl" json:"useSsl" yaml:"use-ssl"`
        Secret   string `mapstructure:"secret" json:"secret" yaml:"secret"`
        Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
    }
    
    type Aliyun struct {
        Path             string `mapstructure:"path" json:"path" yaml:"path"`
        Bucket           string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
        ACLType          string `mapstructure:"acl-type" json:"aclType" yaml:"acl-type"`
        Endpoint         string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
        AccessKeyID      string `mapstructure:"access-key-id" json:"accessKeyId" yaml:"access-key-id"`
        SecretAccessKey  string `mapstructure:"secret-access-key" json:"secretAccessKey" yaml:"secret-access-key"`
        StorageClassType string `mapstructure:"storage-class-type" json:"storageClassType" yaml:"storage-class-type"`
    }
    

    server/config/config.go

    type Server struct {
        JWT   JWT   `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
        Zap   Zap   `mapstructure:"zap" json:"zap" yaml:"zap"`
        Redis Redis `mapstructure:"redis" json:"redis" yaml:"redis"`
        //Email   Email   `mapstructure:"email" json:"email" yaml:"email"`
        //Casbin  Casbin  `mapstructure:"casbin" json:"casbin" yaml:"casbin"`
        System  System  `mapstructure:"system" json:"system" yaml:"system"`
        Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
        // auto
        AutoCode Autocode `mapstructure:"autoCode" json:"autoCode" yaml:"autoCode"`
        // gorm
        Mysql Mysql `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
        //Pgsql  Pgsql `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
        //DBList []DB  `mapstructure:"db-list" json:"db-list" yaml:"db-list"`
        // oss
        Local Local `mapstructure:"local" json:"local" yaml:"local"`
        //Qiniu      Qiniu      `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
        //AliyunOSS AliyunOSS `mapstructure:"aliyun-oss" json:"aliyunOSS" yaml:"aliyun-oss"`
        //HuaWeiObs HuaWeiObs `mapstructure:"hua-wei-obs" json:"huaWeiObs" yaml:"hua-wei-obs"`
        //TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencentCOS" yaml:"tencent-cos"`
        AwsS3 AwsS3 `mapstructure:"aws-s3" json:"awsS3" yaml:"aws-s3"`
        //
        //Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"`
        //Timer Timer `mapstructure:"timer" json:"timer" yaml:"timer"`
        //
        // 跨域配置
        Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
    }
    

    server/config.yaml

    # local configuration
    local:
      path: 'uploads/file'
    
    # qiniu configuration (请自行七牛申请对应的 公钥 私钥 bucket 和 域名地址)
    qiniu:
      zone: 'ZoneHuadong'
      bucket: 'qm-plus-img'
      img-path: 'http://qmplusimg.henrongyi.top'
      use-https: false
      access-key: '25j8dYBZ2wuiy0yhwShytjZDTX662b8xiFguwxzZ'
      secret-key: 'pgdbqEsf7ooZh7W3xokP833h3dZ_VecFXPDeG5JY'
      use-cdn-domains: false
    
    # minio configuration
    minio:
      id: 'minio'
      path: 'http://localhost:9000'
      token: ''
      bucket: 'public'
      use-ssl: false
      secret: 'minio1234'
      endpoint: 'localhost:9000'
    
    # aliyun configuration
    aliyun:
      path : 'https://xxx.oss-cn-beijing.aliyuncs.com'
      bucket : 'xxx'
      acl-type : ''
      endpoint : 'https://oss-cn-beijing.aliyuncs.com'
      access-key-id : 'xxxxxxx'
      secret-access-key : 'xxxxxxx'
      storage-class-type : 'Standard' # 存储桶存储类型
    

    Model:

    server/model/example/exa_file_upload_and_download.go

    package example
    
    import (
        "Project/global"
    )
    
    type ExaFileUploadAndDownload struct {
        global.KBB_MODEL
        Name string `json:"name" gorm:"comment:文件名"` // 文件名
        Url  string `json:"url" gorm:"comment:文件地址"` // 文件地址
        Tag  string `json:"tag" gorm:"comment:文件标签"` // 文件标签
        Key  string `json:"key" gorm:"comment:编号"`   // 编号
    }
    
    func (E ExaFileUploadAndDownload) TableName() string {
        return "exa_file_upload_and_download"
    }
    

    Utils:

    server/utils/upload.go

    package upload
    
    import (
        "mime/multipart"
    
        "Project/global"
    )
    
    // OSS 对象存储接口
    // Author [SliverHorn](https://github.com/SliverHorn)
    // Author [ccfish86](https://github.com/ccfish86)
    type OSS interface {
        UploadFile(file *multipart.FileHeader) (string, string, error)
        DeleteFile(key string) error
    }
    
    // NewOss OSS的实例化方法
    // Author [SliverHorn](https://github.com/SliverHorn)
    // Author [ccfish86](https://github.com/ccfish86)
    func NewOss() OSS {
        switch global.KBB_CONFIG.System.OssType {
        case "local":
            return &Local{}
        case "aws-s3":
            return &AwsS3{}
        default:
            return &Local{}
        }
    }
    

    server/utils/local.go

    package upload
    
    import (
        "errors"
        "io"
        "mime/multipart"
        "os"
        "path"
        "strings"
        "time"
    
        "Project/global"
        "Project/utils"
        "go.uber.org/zap"
    )
    
    type Local struct{}
    
    //@author: [piexlmax](https://github.com/piexlmax)
    //@author: [ccfish86](https://github.com/ccfish86)
    //@author: [SliverHorn](https://github.com/SliverHorn)
    //@object: *Local
    //@function: UploadFile
    //@description: 上传文件
    //@param: file *multipart.FileHeader
    //@return: string, string, error
    
    func (*Local) UploadFile(file *multipart.FileHeader) (string, string, error) {
        // 读取文件后缀
        ext := path.Ext(file.Filename)
        // 读取文件名并加密
        name := strings.TrimSuffix(file.Filename, ext)
        name = utils.MD5V([]byte(name))
        // 拼接新文件名
        filename := name + "_" + time.Now().Format("20060102150405") + ext
        // 尝试创建此路径
        mkdirErr := os.MkdirAll(global.KBB_CONFIG.Local.Path, os.ModePerm)
        if mkdirErr != nil {
            global.KBB_LOG.Error("function os.MkdirAll() Filed", zap.Any("err", mkdirErr.Error()))
            return "", "", errors.New("function os.MkdirAll() Filed, err:" + mkdirErr.Error())
        }
        // 拼接路径和文件名
        p := global.KBB_CONFIG.Local.Path + "/" + filename
    
        f, openError := file.Open() // 读取文件
        if openError != nil {
            global.KBB_LOG.Error("function file.Open() Filed", zap.Any("err", openError.Error()))
            return "", "", errors.New("function file.Open() Filed, err:" + openError.Error())
        }
        defer f.Close() // 创建文件 defer 关闭
    
        out, createErr := os.Create(p)
        if createErr != nil {
            global.KBB_LOG.Error("function os.Create() Filed", zap.Any("err", createErr.Error()))
    
            return "", "", errors.New("function os.Create() Filed, err:" + createErr.Error())
        }
        defer out.Close() // 创建文件 defer 关闭
    
        _, copyErr := io.Copy(out, f) // 传输(拷贝)文件
        if copyErr != nil {
            global.KBB_LOG.Error("function io.Copy() Filed", zap.Any("err", copyErr.Error()))
            return "", "", errors.New("function io.Copy() Filed, err:" + copyErr.Error())
        }
        return p, filename, nil
    }
    
    //@author: [piexlmax](https://github.com/piexlmax)
    //@author: [ccfish86](https://github.com/ccfish86)
    //@author: [SliverHorn](https://github.com/SliverHorn)
    //@object: *Local
    //@function: DeleteFile
    //@description: 删除文件
    //@param: key string
    //@return: error
    
    func (*Local) DeleteFile(key string) error {
        p := global.KBB_CONFIG.Local.Path + "/" + key
        if strings.Contains(p, global.KBB_CONFIG.Local.Path) {
            if err := os.Remove(p); err != nil {
                return errors.New("本地文件删除失败, err:" + err.Error())
            }
        }
        return nil
    }
    

    Service:

    server/service/example/exa_file_upload_download.go

    //@author: [piexlmax](https://github.com/piexlmax)
    //@function: Upload
    //@description: 创建文件上传记录
    //@param: file model.ExaFileUploadAndDownload
    //@return: error
    
    func (e *FileUploadAndDownloadService) Upload(file example.ExaFileUploadAndDownload) error {
        return global.KBB_DB.Create(&file).Error
    }
    
    //@author: [piexlmax](https://github.com/piexlmax)
    //@function: UploadFile
    //@description: 根据配置文件判断是文件上传到本地或者七牛云
    //@param: header *multipart.FileHeader, noSave string
    //@return: err error, file model.ExaFileUploadAndDownload
    
    func (e *FileUploadAndDownloadService) UploadFile(header *multipart.FileHeader, noSave string) (err error, file example.ExaFileUploadAndDownload) {
        oss := upload.NewOss()
        filePath, key, uploadErr := oss.UploadFile(header)
        if uploadErr != nil {
            panic(err)
        }
        if noSave == "0" {
            s := strings.Split(header.Filename, ".")
            f := example.ExaFileUploadAndDownload{
                Url:  filePath,
                Name: header.Filename,
                Tag:  s[len(s)-1],
                Key:  key,
            }
            return e.Upload(f), f
        }
        return
    }
    

    Controller:

    server/api/exa_file_upload_download.go

    // UploadFile @Tags ExaFileUploadAndDownload
    // @Summary 上传文件示例
    // @Security ApiKeyAuth
    // @accept multipart/form-data
    // @Produce  application/json
    // @Param file formData file true "上传文件示例"
    // @Success 200 {object} response.Response{data=exampleRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情"
    // @Router /fileUploadAndDownload/upload [post]
    func (u *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
       var file example.ExaFileUploadAndDownload
       noSave := c.DefaultQuery("noSave", "0")
       _, header, err := c.Request.FormFile("file")
       if err != nil {
          global.KBB_LOG.Error("接收文件失败!", zap.Error(err))
          utils.FailWithMessage("接收文件失败", c)
          return
       }
       err, file = fileUploadAndDownloadService.UploadFile(header, noSave) // 文件上传后拿到文件路径
       if err != nil {
          global.KBB_LOG.Error("修改数据库链接失败!", zap.Error(err))
          utils.FailWithMessage("修改数据库链接失败", c)
          return
       }
       utils.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c)
    }
    

    接口测试:

    [图片上传失败...(image-861889-1651247061028)]

    [图片上传失败...(image-34a857-1651247061028)]

    由于资金有限,甲方没有提供OSS服务器,所以此接口,只用于上传到local。
    gin-vue-admin
    官方提供了qiniu、minio、aliyun等utils工具。

    CSDN: 基于Go语言实现OSS文件上传(local)

    欢迎群共同进步:
    QQ群:1007576722

    相关文章

      网友评论

        本文标题:基于Go语言实现OSS文件上传(local)

        本文链接:https://www.haomeiwen.com/subject/wldeyrtx.html