upload_tencent_cos.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * @desc:腾讯oss
  3. * @company:云南省奇讯科技有限公司
  4. * @Author: yixiaohu
  5. * @Date: 2021/7/7 15:51
  6. */
  7. package adapter
  8. import (
  9. "context"
  10. "gfast/app/common/model"
  11. "gfast/app/common/service"
  12. "github.com/gogf/gf/errors/gerror"
  13. "github.com/gogf/gf/frame/g"
  14. "github.com/gogf/gf/net/ghttp"
  15. "github.com/gogf/gf/os/gfile"
  16. "github.com/gogf/gf/os/gtime"
  17. "github.com/gogf/gf/text/gregex"
  18. "github.com/gogf/gf/text/gstr"
  19. "github.com/gogf/gf/util/gconv"
  20. "github.com/gogf/gf/util/grand"
  21. "github.com/tencentyun/cos-go-sdk-v5"
  22. "github.com/tencentyun/cos-go-sdk-v5/debug"
  23. "io"
  24. "net/http"
  25. "net/url"
  26. "strconv"
  27. "strings"
  28. )
  29. func init() {
  30. var adp UploadAdapter
  31. if upType == "tencentCOS" {
  32. //使用腾讯云COS上传
  33. adp = UploadTencentCOSAdapter{
  34. UpPath: g.Cfg().GetString("upload.tencentCOS.UpPath"),
  35. RawUrl: g.Cfg().GetString("upload.tencentCOS.RawUrl"),
  36. SecretID: g.Cfg().GetString("upload.tencentCOS.SecretID"),
  37. SecretKey: g.Cfg().GetString("upload.tencentCOS.SecretKey"),
  38. }
  39. Upload = &upload{
  40. adapter: adp,
  41. }
  42. }
  43. }
  44. type UploadTencentCOSAdapter struct {
  45. UpPath string
  46. SecretID string
  47. SecretKey string
  48. RawUrl string
  49. }
  50. func (u UploadTencentCOSAdapter) UpImg(file *ghttp.UploadFile) (fileInfo *FileInfo, err error) {
  51. return u.upByType(file, "img")
  52. }
  53. func (u UploadTencentCOSAdapter) UpFile(file *ghttp.UploadFile) (fileInfo *FileInfo, err error) {
  54. return u.upByType(file, "file")
  55. }
  56. func (u UploadTencentCOSAdapter) UpImgs(files []*ghttp.UploadFile) (fileInfos []*FileInfo, err error) {
  57. return u.upBathByType(files, "img")
  58. }
  59. func (u UploadTencentCOSAdapter) UpFiles(files []*ghttp.UploadFile) (fileInfos []*FileInfo, err error) {
  60. return u.upBathByType(files, "file")
  61. }
  62. //文件上传 img|file
  63. func (u UploadTencentCOSAdapter) upByType(file *ghttp.UploadFile, fType string) (fileInfo *FileInfo, err error) {
  64. if file == nil {
  65. err = gerror.New("未上传任何文件")
  66. return
  67. }
  68. var (
  69. typeKey string
  70. sizeKey string
  71. )
  72. if fType == "img" {
  73. typeKey = "sys.uploadFile.imageType"
  74. sizeKey = "sys.uploadFile.imageSize"
  75. } else if fType == "file" {
  76. typeKey = "sys.uploadFile.fileType"
  77. sizeKey = "sys.uploadFile.fileSize"
  78. }
  79. //获取上传类型配置
  80. config, err := u.getUpConfig(typeKey)
  81. if err != nil {
  82. return
  83. }
  84. //检测文件类型
  85. rightType := u.checkFileType(file.Filename, config.ConfigValue)
  86. if !rightType {
  87. err = gerror.New("上传文件类型错误,只能包含后缀为:" + config.ConfigValue + "的文件。")
  88. return
  89. }
  90. //获取上传大小配置
  91. config, err = u.getUpConfig(sizeKey)
  92. if err != nil {
  93. return
  94. }
  95. rightSize, err := u.checkSize(config.ConfigValue, file.Size)
  96. if err != nil {
  97. return
  98. }
  99. if !rightSize {
  100. err = gerror.New("上传文件超过最大尺寸:" + config.ConfigValue)
  101. return
  102. }
  103. var path string
  104. path, err = u.upAction(file)
  105. if err != nil {
  106. return
  107. }
  108. fileInfo = &FileInfo{
  109. FileName: file.Filename,
  110. FileSize: file.Size,
  111. FileUrl: u.getUrl(path),
  112. FileType: file.Header.Get("Content-type"),
  113. }
  114. return
  115. }
  116. //批量上传 img|file
  117. func (u UploadTencentCOSAdapter) upBathByType(files []*ghttp.UploadFile, fType string) (fileInfos []*FileInfo, err error) {
  118. if len(files) == 0 {
  119. err = gerror.New("未上传任何文件")
  120. return
  121. }
  122. var (
  123. typeKey string
  124. sizeKey string
  125. )
  126. if fType == "img" {
  127. typeKey = "sys.uploadFile.imageType"
  128. sizeKey = "sys.uploadFile.imageSize"
  129. } else if fType == "file" {
  130. typeKey = "sys.uploadFile.fileType"
  131. sizeKey = "sys.uploadFile.fileSize"
  132. }
  133. //获取上传类型配置
  134. configType, err := u.getUpConfig(typeKey)
  135. if err != nil {
  136. return
  137. }
  138. //获取上传大小配置
  139. configSize, err := u.getUpConfig(sizeKey)
  140. if err != nil {
  141. return
  142. }
  143. for _, file := range files {
  144. //检测文件类型
  145. rightType := u.checkFileType(file.Filename, configType.ConfigValue)
  146. if !rightType {
  147. err = gerror.New("上传文件类型错误,只能包含后缀为:" + configType.ConfigValue + "的文件。")
  148. return
  149. }
  150. var rightSize bool
  151. rightSize, err = u.checkSize(configSize.ConfigValue, file.Size)
  152. if err != nil {
  153. return
  154. }
  155. if !rightSize {
  156. err = gerror.New("上传文件超过最大尺寸:" + configSize.ConfigValue)
  157. return
  158. }
  159. }
  160. for _, file := range files {
  161. var path string
  162. path, err = u.upAction(file)
  163. if err != nil {
  164. return
  165. }
  166. fileInfo := &FileInfo{
  167. FileName: file.Filename,
  168. FileSize: file.Size,
  169. FileUrl: u.getUrl(path),
  170. FileType: file.Header.Get("Content-type"),
  171. }
  172. fileInfos = append(fileInfos, fileInfo)
  173. }
  174. return
  175. }
  176. // 上传到腾讯cos操作
  177. func (u UploadTencentCOSAdapter) upAction(file *ghttp.UploadFile) (path string, err error) {
  178. name := gfile.Basename(file.Filename)
  179. name = strings.ToLower(strconv.FormatInt(gtime.TimestampNano(), 36) + grand.S(6))
  180. name = name + gfile.Ext(file.Filename)
  181. path = u.getUpPath() + name
  182. url, _ := url.Parse(u.RawUrl)
  183. b := &cos.BaseURL{BucketURL: url}
  184. c := cos.NewClient(b, &http.Client{
  185. Transport: &cos.AuthorizationTransport{
  186. SecretID: u.SecretID,
  187. SecretKey: u.SecretKey,
  188. Transport: &debug.DebugRequestTransport{
  189. RequestHeader: false,
  190. RequestBody: false,
  191. ResponseHeader: false,
  192. ResponseBody: false,
  193. },
  194. },
  195. })
  196. opt := &cos.ObjectPutOptions{
  197. ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
  198. ContentLength: int64(file.Size),
  199. },
  200. }
  201. var f io.ReadCloser
  202. f, err = file.Open()
  203. if err != nil {
  204. return
  205. }
  206. defer f.Close()
  207. _, err = c.Object.Put(context.Background(), path, f, opt)
  208. return
  209. }
  210. //获取上传配置
  211. func (u UploadTencentCOSAdapter) getUpConfig(key string) (config *model.SysConfig, err error) {
  212. config, err = service.SysConfig.GetConfigByKey(key)
  213. if err != nil {
  214. return
  215. }
  216. if config == nil {
  217. err = gerror.New("上传文件类型未设置,请在后台配置")
  218. return
  219. }
  220. return
  221. }
  222. //判断上传文件类型是否合法
  223. func (u UploadTencentCOSAdapter) checkFileType(fileName, typeString string) bool {
  224. suffix := gstr.SubStrRune(fileName, gstr.PosRRune(fileName, ".")+1, gstr.LenRune(fileName)-1)
  225. imageType := gstr.Split(typeString, ",")
  226. rightType := false
  227. for _, v := range imageType {
  228. if gstr.Equal(suffix, v) {
  229. rightType = true
  230. break
  231. }
  232. }
  233. return rightType
  234. }
  235. //检查文件大小是否合法
  236. func (u UploadTencentCOSAdapter) checkSize(configSize string, fileSize int64) (bool, error) {
  237. match, err := gregex.MatchString(`^([0-9]+)(?i:([a-z]*))$`, configSize)
  238. if err != nil {
  239. return false, err
  240. }
  241. if len(match) == 0 {
  242. err = gerror.New("上传文件大小未设置,请在后台配置,格式为(30M,30k,30MB)")
  243. return false, err
  244. }
  245. var cfSize int64
  246. switch gstr.ToUpper(match[2]) {
  247. case "MB", "M":
  248. cfSize = gconv.Int64(match[1]) * 1024 * 1024
  249. case "KB", "K":
  250. cfSize = gconv.Int64(match[1]) * 1024
  251. case "":
  252. cfSize = gconv.Int64(match[1])
  253. }
  254. if cfSize == 0 {
  255. err = gerror.New("上传文件大小未设置,请在后台配置,格式为(30M,30k,30MB),最大单位为MB")
  256. return false, err
  257. }
  258. return cfSize >= fileSize, nil
  259. }
  260. func (u UploadTencentCOSAdapter) getUpPath() (upPath string) {
  261. upPath = u.UpPath + gtime.Date() + "/"
  262. return
  263. }
  264. func (u UploadTencentCOSAdapter) getUrl(path string) string {
  265. url := u.RawUrl + path
  266. return url
  267. }