前言

基于gin-web框架开发GoFly快速开发框架v3.01版本已更新,本次更新最大亮点是把多种文件存储方式集成在框架中,放弃之前单一种方式做成独立插件安装,单个插件虽然有代码少的优点,但是在实际业务开发时,当切换其他存储位置时,之前存储位置文件无法访问,且代码结构相对零散。所以从实现业务需求出发,我们决定把多种存储方式集成在一起,统一代码结构,统一调用,后台统一管理,切换方便。

在软件开发中文件上传存储、访问文件是一个必不可少的功能,好用文件存储管理可让开发省去很多的时间,这也是GoFly框架本次更新重视文件文件存储管理的原因。

如下图GoFly后台的文件存储配置,开发时可以直接通过后台配置不同存储位置参数,点“文件存储方式”切换并提交“保存”即可完成不同文件存储位置变更。

在线体验地址:​​https://spl.goflys.cn/webbusiness/​​ ,账号:gofly   密码:gofly123

开发使用方法

框架把多种文件存储位置集成后,提供给业务开发统一调用函数,只需在后台选择存储位置和配置相关参数即可,代码部分就不用单独处理。

统一文件处理模块引入:

import (
	"gofly/utils/extend/uploads"
)

1.统一上传文件函数

函数名:UploadFile

参数:c请求上下文,file上传文件流

返回:url是返回文件路径

//处理文件上传,bin返回地址
url, cover_url, err := uploads.New().UploadFile(c, file)

注意:如果在选择文件上传云服务器,但某个功能文件需要单独本地存储,则New()添加参数如下:

//处理文件上传,bin返回地址
url, cover_url, err := uploads.New("local").UploadFile(c, file)
if err != nil {
  gf.Failed().SetMsg("上传文件失败").SetData(err).Regin(c)
  return
}

其中New("local")参数local可以改为:alioss、tencentcos、qiniuoss等其他云存储名。

2.统一删除文件函数

函数名:DelFile

参数:url是删除文件路径,不包含域名的路径。

err:=uploads.DelFile(url)

3.示例代码

实际调用上传函数实例代码:


// 上传文件
func (api *Upfile) LocalFile(c *gf.GinCtx) {
	//file文件流
	file, err := c.FormFile("file")
	if err != nil {
		gf.Failed().SetMsg("获取数据失败").SetData(err).Regin(c)
		return
	}
	//判断文件是否已经传过
	fileContent, _ := file.Open()
	defer fileContent.Close()
	var byteContainer []byte = make([]byte, 1000000)
	fileContent.Read(byteContainer)
	m_d5 := md5.New()
	m_d5.Write(byteContainer)
	sha1_str := hex.EncodeToString(m_d5.Sum(nil))
	//查找该用户是否传过
	attachment, _ := gf.Model("business_attachment").Where("business_id", businessID).
		Where("sha1", sha1_str).Fields("id,pid,name,title,type,url,filesize,mimetype,cover_url as cover").Find()
	if attachment != nil { //文件是否已经存在
		//更新到最前面
		maxId, _ := gf.Model("business_attachment").Where("business_id", businessID).Order("weigh desc").Value("id")
		if maxId != nil {
			gf.Model("business_attachment").Data(map[string]interface{}{"weigh": maxId.Int() + 1, "pid": 1}).Where("id", attachment["id"]).Update()
		}
		gf.Success().SetMsg("文件已上传").SetData(attachment).Regin(c)
	} else {
		//这里处理文件上传 并返回地址, uploads.New().UploadFile就是把文件上传到配置设定位置
		url, cover_url, err := uploads.New().UploadFile(c, file)
		if err != nil {
			gf.Failed().SetMsg("上传文件失败").SetData(err).Regin(c)
			return
		}
		filename_arr := strings.Split(file.Filename, ".")
		//文件类型
		fileData := gf.Map{
			"type":        0, //图片
			"pid":         1, //存储在默认文件夹
			"sha1":        sha1_str,
			"title":       filename_arr[0],
			"name":        file.Filename,
			"url":         url,       //附件路径
			"cover_url":   cover_url, //封面
			"storage":     "local",
			"createtime":  time.Now().Unix(),
			"filesize":    file.Size,
			"mimetype":    file.Header["Content-Type"][0],
		}
		//保存数据
		file_id, _ := gf.Model("business_attachment").Data(fileData).InsertAndGetId()
		//更新排序
		gf.Model("business_attachment").Data(map[string]interface{}{"weigh": file_id}).Where("id", file_id).Update()
		//处理预览url地址
		gf.Success().SetMsg("文件上传成功").SetData(gf.GetFullUrl(url)).Regin(c)
	}
}

如果某个功能需单独存储在指定位置,和系统设置的全局存储位置不同,比如把使用频率或者访问速度要求较低的,我们可以把他存储在本地,那么把uploads.New("local").UploadFile(c, file)的New函数中添加存储位置名,本地存储名为:local 则上传本地调用的代码如下:

//处理文件上传,并返回地址
url, cover_url, err := uploads.New("local").UploadFile(c, file)
if err != nil {
	gf.Failed().SetMsg("上传文件失败").SetData(err).Regin(c)
	return
}

4.框架封装好的上传文件接口

框架已经为各端客户端封装好了,可以直接调用上传文件接口,使用GoFly框架开发时没有特殊要求就不需要写上传文件接口代码,直接调用现成就实现上传。

4.1管理后后台(admin端)

接口:域名+admin/datacenter/upfile/upload

4.2管理后后台(business端)

接口:域名+business/datacenter/upfile/upload

4.3 非管理后台客户端(微信小程序、app、h5应用)

接口:域名+common/upload/upfile

继续扩展其他存储指导

如果框架已经集成的存储方式没有你需要的,可以参考现有的存储代码继续拓展。集成上传文件的代码位置在:utils\extend\uploads,在uploads目录下有个index.go是统一调用函数入口及配置存储代码。目录下的local.go为本地上传功能代码、aliOSS.go为阿里云存储功能代码、tencentCOS.go为腾讯云存储功能代码、qiniuOSS.go为七牛云存储功能代码,开发新的存储方式时可以参考他们。

下面介绍集成新的上传方式步骤,这里以华为云存储为例演示:

1.创建存储代码文件

在utils\extend\uploads创建一个名为:huaweiobs.go 代码框架如下:

package uploads

import (
	"errors"
	"fmt"
	"gofly/utils/gf"
	"mime/multipart"
	"path"
	"path/filepath"
	"time"

	 obs "github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
)

// 华为对象存储服务 OBS

type HuaweiObs struct {
	Client *obs.Client//具体更加华为提供SKD为准
	Ready  bool
	Config Config
	Bucket *obs.Bucket//具体更加华为提供SKD为准
}

func (m *HuaweiObs ) InitClient(config Config) {
	m.Ready = false
	m.Config = config
	client, err := oss.New(config.Endpoint, config.KeyId, config.Secret)
	if err != nil {
		return
	}
	m.Client = client
	m.Bucket, err = client.Bucket(config.BucketName)
	if err != nil {
		return
	}
	m.Ready = true
}

// 上传文件
func (m *HuaweiObs ) UploadFile(c *gf.GinCtx, file *multipart.FileHeader) (url, cover_url string, err error) {
	if !m.Ready {
		err = errors.New("not ready")
		return
	}
	fd, err := file.Open()
	if err != nil {
		return
	}
	defer fd.Close()
	//注意:文件名称必须以alioss开头,MakeFileName函数就是生成以huaweiobs 开头的文件名称
	path_name := fmt.Sprintf("%v/%v/%v", m.Config.DirPath, time.Now().Format("20060102"), MakeFileName(file, "huaweiobs"))
	url = path_name
	err = m.Bucket.PutObject(path_name, fd)
	return
}

// 删除文件(单个)
func (m *HuaweiObs ) RemoveFile(fileUrl string) error {
	if !m.Ready {
		return errors.New("not ready")
	}
	return m.Bucket.DeleteObject(fileUrl)
}

// 下载文件
func (m *HuaweiObs ) DownloadFile(fileUrl string) error {
	fileUrl, cloudFileUrl := InitFileUrl(fileUrl, m.Config)
	if !m.Ready {
		return errors.New("not ready")
	}
	// 检查对象是否存在
	_, err := m.Bucket.GetObjectMeta(cloudFileUrl)
	if err != nil {
		return err
	}
	// 下载文件
	return m.Bucket.GetObjectToFile(cloudFileUrl, fileUrl)
}

// 移动文件
func (m *HuaweiObs ) MoveFile(fileUrl string, targerDir string) (string, error) {

	// 01. 拷贝
	_, fileUrl = InitFileUrl(fileUrl, m.Config)
	_, targerUrl = InitFileUrl(targerUrl, m.Config)
	_, err := m.Bucket.CopyObject(fileUrl, targerUrl)
	if err != nil {
		return "", err
	}
	// 02. 删除
	err = m.Bucket.DeleteObject(fileUrl)
	if err != nil {
		return "", err
	}
	return targerUrl, nil
}

2.在index.go引入华为云存储功能

写好上传、删除等相关接口后,需要把huaweiobs.go在统一调用index.go文件注册,具体如下

2.1在New初始配置函数中添加

func New(uptype ...string) StaticCloud {
	var staticCloud StaticCloud
	var upTypeStr string = ""
	var config = Config{}
	if len(uptype) > 0 && uptype[0] != "" {
		upTypeStr = uptype[0]
	} else {
		confType, _ := gcfg.Instance("upload").Get(ctx, "Type")
		upTypeStr = confType.String()
	}
	switch upTypeStr {
	case "alioss":
		alioss, _ := gcfg.Instance("upload").Get(ctx, "alioss")
		mapConf := alioss.Map()
		config = Config{
			BaseUrl:    gf.String(mapConf["ABaseUrl"]),
			Endpoint:   gf.String(mapConf["AEndpoint"]),
			KeyId:      gf.String(mapConf["AKeyId"]),
			Secret:     gf.String(mapConf["ASecret"]),
			BucketName: gf.String(mapConf["ABucketName"]),
			DirPath:    gf.String(mapConf["ADirPath"]),
		}
		staticCloud = &AliOSS{}
	case "tencentcos":
		tencentcos, _ := gcfg.Instance("upload").Get(ctx, "tencentcos")
		mapConf := tencentcos.Map()
		config = Config{
			BaseUrl:    gf.String(mapConf["TBaseUrl"]),
			Endpoint:   gf.String(mapConf["TEndpoint"]),
			KeyId:      gf.String(mapConf["TKeyId"]),
			Secret:     gf.String(mapConf["TSecret"]),
			BucketName: gf.String(mapConf["TBucketName"]),
			Region:     gf.String(mapConf["TRegion"]),
			DirPath:    gf.String(mapConf["TDirPath"]),
		}
		staticCloud = &TencentCOS{}
	case "qiniuoss":
		qiniuoss, _ := gcfg.Instance("upload").Get(ctx, "qiniuoss")
		mapConf := qiniuoss.Map()
		config = Config{
			BaseUrl:    gf.String(mapConf["QBaseUrl"]),
			Endpoint:   gf.String(mapConf["QEndpoint"]),
			KeyId:      gf.String(mapConf["QKeyId"]),
			Secret:     gf.String(mapConf["QSecret"]),
			BucketName: gf.String(mapConf["QBucketName"]),
			DirPath:    gf.String(mapConf["QDirPath"]),
			UseHTTPS:   gf.Bool(mapConf["QUseHTTPS"]),
			Zone:       gf.Int(mapConf["QZone"]),
		}
		staticCloud = &QiniuOSS{}
        case "huaweiobs":
		huaweiobs, _ := gcfg.Instance("upload").Get(ctx, "huaweiobs")
		mapConf := huaweiobs.Map()
		config = Config{
			BaseUrl:    gf.String(mapConf["HBaseUrl"]),
			Endpoint:   gf.String(mapConf["HEndpoint"]),
			KeyId:      gf.String(mapConf["HKeyId"]),
			Secret:     gf.String(mapConf["HSecret"]),
			BucketName: gf.String(mapConf["HBucketName"]),
			DirPath:    gf.String(mapConf["HDirPath"]),
		}
		staticCloud = &HuaweiObs{}
	default:
		local, _ := gcfg.Instance("upload").Get(ctx, "local")
		mapConf := local.Map()
		config = Config{
			BaseUrl: gf.String(mapConf["LBaseUrl"]),
			DirPath: gf.String(mapConf["LDirPath"]),
		}
		staticCloud = &Local{}
	}
	staticCloud.InitClient(config)
	return staticCloud
}

2.2配置统一删除

// 删除附件统一入口,结果返回:bool
func DelFile(fileUrl string) error {
	//处理地址
	filseName := gfile.Name(fileUrl)
	uptype := ""
	if strings.HasPrefix(filseName, "local") { //本地存储
		uptype = "local"
	} else if strings.HasPrefix(filseName, "alioss") { //阿里云
		uptype = "alioss"
	} else if strings.HasPrefix(filseName, "tencentcos") { //腾讯云
		uptype = "tencentcos"
	} else if strings.HasPrefix(filseName, "qiniuoss") { //七牛云
		uptype = "qiniuoss"
	}else if strings.HasPrefix(filseName, "huaweiobs") { //华为云
		uptype = "huaweiobs"
	} else { //默认返回设置上传方式的地址
		UpType, _ := gcfg.Instance("upload").Get(ctx, "Type")
		uptype = UpType.String()
	}
	return New(uptype).RemoveFile(fileUrl)
}

3.添加配置文件

在后端资源目录:resource\config\upload.yaml添加华为云存储配置参数:

# 存储方式
Type: local
# 配置传输文件最大值,如上传文件最大上限,单位为MB
MaxBodySize: 600
# 允许上传的文件类型
AllowedExt: .jpg,.jpeg,.png,.pdf,.ico
# 本地存储配置
local: 
  LBaseUrl: http://localhost:8200
  LDirPath: /resource/uploads/
# 阿里云静态云存储配置
alioss:
  ABaseUrl: https://hcoder.oss-cn-beijing.aliyuncs.com/
  AEndpoint: oss-cn-beijing.aliyuncs.com
  AKeyId: 
  ASecret: 
  ABucketName: 
  ADirPath: 
# 腾讯云静态云存储配置
tencentcos:
  TBaseUrl: https://gofly-1257246782.cos.ap-nanjing.myqcloud.com
  TEndpoint: 
  TKeyId: AKIDGUYaP1TmtpY4NSJxlWK0eAbzEKEvRkw0
  TSecret: 
  TBucketName: gofly-1257246782
  TRegion: ap-nanjing
  TDirPath: /goflys
# 七牛云存储配置
qiniuoss:
  QBaseUrl: http://t3wcppubp.hn-bkt.clouddn.com/
  QEndpoint: 
  QKeyId: dtxSwoGm1hwkn5FvhEI8QBvIKxn08_5iYypjC4u8
  QSecret: 
  QBucketName: goflys
  QDirPath: image
  QDestBucketName: <nil>
  QUseHTTPS: false
  QZone: 4
# 华为云存储配置(具体参数根据华为云调整)
huaweiobs:
  HBaseUrl: https://obs.cn-north-4.myhuaweicloud.com
  HEndpoint: obs.cn-north-4.myhuaweicloud.com
  HKeyId: 
  HSecret: 
  HBucketName: 
  HDirPath: 

4.把新增存储方式添加管理后台

后端添加好后,需要把新增加存储方式添加到管理后台,方便配置和管理。在管理后台前端代码位置:src\views\datacenter\configuration\components\UploadConfig.vue添加代码:

在js代码中handleChangeType函数条件华为云的数据配置,代码如下

    //切换上传方式
    const handleChangeType=(value:any)=>{
      if(value=="local"){
        formData.value=Object.assign({},formData.value,{BaseUrl:baseData.local.LBaseUrl,DirPath:baseData.local.LDirPath})
      }else if(value=="tencentcos"){
        formData.value=Object.assign({},formData.value,{BaseUrl:baseData.tencentcos.TBaseUrl,Endpoint:baseData.tencentcos.TEndpoint,
          KeyId:baseData.tencentcos.TKeyId,Secret:baseData.tencentcos.TSecret,BucketName:baseData.tencentcos.TBucketName,
          Region:baseData.tencentcos.TRegion,DirPath:baseData.tencentcos.TDirPath})
      }else if(value=="alioss"){
        formData.value=Object.assign({},formData.value,{BaseUrl:baseData.alioss.ABaseUrl,Endpoint:baseData.alioss.AEndpoint,
          KeyId:baseData.alioss.AKeyId,Secret:baseData.alioss.ASecret,BucketName:baseData.alioss.ABucketName,
          DirPath:baseData.alioss.ADirPath})
      }else if(value=="qiniuoss"){
        formData.value=Object.assign({},formData.value,{BaseUrl:baseData.qiniuoss.QBaseUrl,Endpoint:baseData.qiniuoss.QEndpoint,
          KeyId:baseData.qiniuoss.QKeyId,Secret:baseData.qiniuoss.QSecret,BucketName:baseData.qiniuoss.QBucketName,
        DestBucketName:baseData.qiniuoss.QDestBucketName,UseHTTPS:baseData.qiniuoss.QUseHTTPS,Zone:baseData.qiniuoss.QZone,
        DirPath:baseData.qiniuoss.QDirPath})
      }else if(value=="huaweiobs"){//新增的华为云
        formData.value=Object.assign({},formData.value,{BaseUrl:baseData.huaweiobs.HBaseUrl,Endpoint:baseData.huaweiobs.HEndpoint,
          KeyId:baseData.huaweiobs.HKeyId,Secret:baseData.huaweiobs.ASecret,BucketName:baseData.huaweiobs.HBucketName,
          DirPath:baseData.huaweiobs.HDirPath})
      }
      console.log(formData.value)
    }

到此我们就把扩展新存储介绍完了,大家可以通过本文介绍了解,Gin集成的GoFly框架文件存储使用和继续扩展新存储方式的方法。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐