前言
基于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
Gin集成阿里云OSS对象存储指导
如果你想了解如何集成阿里云存储,现在到项目打开路径:utils\extend\uploads,在uploads目录下有个index.go是统一调用函数入口及配置存储代码。目录下的local.go为本地上传功能代码、aliOSS.go为阿里云存储功能代码等云存储功能代码。下面介绍阿里云存储集成步骤:
1.创建存储代码文件
在utils\extend\uploads创建一个名为:aliOSS.go 代码框架如下:
package uploads import ( "errors" "fmt" "gofly/utils/gf" "mime/multipart" "path" "path/filepath" "time" "github.com/aliyun/aliyun-oss-go-sdk/oss" ) // 阿里云 OSS 文档 // https://help.aliyun.com/zh/oss/developer-reference/introduction-3?spm=5176.8466032.console-base_help.dexternal.67181450WWM1Nx type AliOSS struct { Client *oss.Client Ready bool Config Config Bucket *oss.Bucket } func (m *AliOSS) 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 *AliOSS) 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函数就是生成以alioss开头的文件名称 path_name := fmt.Sprintf("%v/%v/%v", m.Config.DirPath, time.Now().Format("20060102"), MakeFileName(file, "alioss")) url = path_name err = m.Bucket.PutObject(path_name, fd) return } // 删除文件(单个) func (m *AliOSS) RemoveFile(fileUrl string) error { if !m.Ready { return errors.New("not ready") } return m.Bucket.DeleteObject(fileUrl) } // 下载文件 func (m *AliOSS) 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 *AliOSS) MoveFile(fileUrl string, targerDir string) (string, error) { // 本地移动 fileUrl, _ = InitFileUrl(fileUrl, m.Config) targerUrl := path.Join(targerDir, filepath.Base(fileUrl)) targerUrl, _ = InitFileUrl(targerUrl, m.Config) MoveFile(fileUrl, targerUrl) // 阿里云移动 // 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引入阿里云存储功能
写好上传、删除等相关接口后,需要把aliOSS.go在统一调用index.go文件注册,具体如下
2.1在New初始配置函数中添加
// 初始配置,如果传uptype(上传方式),则直接示例传入上传方式,不传则从配置文件获取设置的上传方式 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{} 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 { //默认返回设置上传方式的地址 UpType, _ := gcfg.Instance("upload").Get(ctx, "Type") uptype = UpType.String() } return New(uptype).RemoveFile(fileUrl) }
3.添加配置文件
在后端资源目录:resource\config\upload.yaml添加阿里云存储配置参数:
# 存储方式 Typelocal # 配置传输文件最大值,如上传文件最大上限,单位为MB MaxBodySize600 # 允许上传的文件类型 AllowedExt.jpg,.jpeg,.png,.pdf,.ico # 本地存储配置 local LBaseUrlhttp//localhost8200 LDirPath/resource/uploads/ # 阿里云静态云存储配置 alioss ABaseUrlhttps//hcoder.oss-cn-beijing.aliyuncs.com/ AEndpointoss-cn-beijing.aliyuncs.com AKeyId ASecret ABucketName ADirPath # 腾讯云静态云存储配置 tencentcos TBaseUrlhttps//gofly-1257246782.cos.ap-nanjing.myqcloud.com TEndpoint TKeyIdAKIDGUYaP1TmtpY4NSJxlWK0eAbzEKEvRkw0 TSecret TBucketNamegofly-1257246782 TRegionap-nanjing TDirPath/goflys # 七牛云存储配置 qiniuoss QBaseUrlhttp//t3wcppubp.hn-bkt.clouddn.com/ QEndpoint QKeyIddtxSwoGm1hwkn5FvhEI8QBvIKxn08_5iYypjC4u8 QSecret QBucketNamegoflys QDirPathimage QDestBucketName<nil> QUseHTTPSfalse QZone4
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}) } console.log(formData.value) }
到此我们就把阿里云存储介绍完了,大家可以通过本文介绍了解,Gin集成的GoFly框架文件存储使用和继续扩展新存储方式的方法。