file_oss.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. package config
  2. import (
  3. "encoding/csv"
  4. "encoding/json"
  5. "fmt"
  6. "github.com/aliyun/aliyun-oss-go-sdk/oss"
  7. "github.com/aws/aws-sdk-go/aws"
  8. "github.com/aws/aws-sdk-go/aws/credentials"
  9. "github.com/aws/aws-sdk-go/aws/session"
  10. "github.com/aws/aws-sdk-go/service/s3"
  11. "github.com/aws/aws-sdk-go/service/s3/s3manager"
  12. "github.com/pkg/errors"
  13. "github.com/xuri/excelize/v2"
  14. "go_server/utils"
  15. "io"
  16. "mime/multipart"
  17. "os"
  18. "path/filepath"
  19. "reflect"
  20. "strings"
  21. "sync"
  22. "time"
  23. )
  24. type File struct {
  25. OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"` // 文件存储类型
  26. Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件访问路径
  27. ProxyPath string `mapstructure:"proxy-path" json:"proxy-path" yaml:"proxy-path"` // 系统代理路径
  28. StorePath string `mapstructure:"store-path" json:"store-path" yaml:"store-path"` // 本地文件存储路径
  29. OriginConf map[string]interface{} `mapstructure:"origin-conf" json:"origin-conf" yaml:"origin-conf"` // 远程文件存储配置
  30. }
  31. // 导出csv表
  32. func ExportCsv(headers []string, data [][]string, filename string) (string, error) {
  33. if filename == "" || len(data) == 0 {
  34. return "", fmt.Errorf("数据错误")
  35. }
  36. // exportCSV 导出CSV文件
  37. // 生成文件名,包含当前时间戳
  38. // 创建文件
  39. file, err := os.Create(filename)
  40. if err != nil {
  41. return "", fmt.Errorf("创建文件失败: %v", err)
  42. }
  43. defer file.Close()
  44. file.WriteString("\xEF\xBB\xBF")
  45. // 创建CSV writer
  46. writer := csv.NewWriter(file)
  47. defer writer.Flush()
  48. // 写入表头
  49. if err := writer.Write(headers); err != nil {
  50. return "", fmt.Errorf("写入表头失败: %v", err)
  51. }
  52. // 写入数据
  53. for _, record := range data {
  54. if err := writer.Write(record); err != nil {
  55. return "", fmt.Errorf("写入数据失败: %v", err)
  56. }
  57. }
  58. return filename, nil
  59. }
  60. func ExportExcelFile[T any](headers []string, data []T, savePath string) error {
  61. if savePath == "" || len(data) == 0 {
  62. return fmt.Errorf("无目标数据")
  63. }
  64. f := excelize.NewFile()
  65. sheetName := "Sheet1"
  66. if err := f.SetSheetName("Sheet1", sheetName); err != nil {
  67. return err
  68. }
  69. // 写入表头
  70. for i, header := range headers {
  71. cell, _ := excelize.CoordinatesToCellName(i+1, 1)
  72. if err := f.SetCellValue(sheetName, cell, header); err != nil {
  73. return err
  74. }
  75. }
  76. // 使用反射获取结构体字段值
  77. for rowIndex, row := range data {
  78. rowValue := reflect.ValueOf(row)
  79. for colIndex := 0; colIndex < rowValue.NumField(); colIndex++ {
  80. cell, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+2)
  81. field := rowValue.Field(colIndex)
  82. if field.CanInterface() {
  83. if err := f.SetCellValue(sheetName, cell, field.Interface()); err != nil {
  84. return err
  85. }
  86. }
  87. }
  88. }
  89. // 保存 Excel 文件
  90. if err := f.SaveAs(savePath); err != nil {
  91. return err
  92. }
  93. return nil
  94. }
  95. func (a *File) UploadFile(file *multipart.FileHeader) (viewPath, savePath string, err error) {
  96. switch a.OssType {
  97. case OssTypeLocal:
  98. viewPath, savePath, err = a.uploadFile(file)
  99. return
  100. case OssTypeAws:
  101. engine := new(AwsS3)
  102. byteData, _ := json.Marshal(a.OriginConf)
  103. err = json.Unmarshal(byteData, &engine)
  104. if err != nil {
  105. return
  106. }
  107. viewPath, savePath, err = engine.UploadFile(file)
  108. case OssTypeAli:
  109. engine := new(AliOSS)
  110. byteData, _ := json.Marshal(a.OriginConf)
  111. err = json.Unmarshal(byteData, &engine)
  112. if err != nil {
  113. return
  114. }
  115. viewPath, savePath, err = engine.UploadFile(file)
  116. return
  117. default:
  118. err = fmt.Errorf("oss type not support")
  119. break
  120. }
  121. return
  122. }
  123. func (a *File) DeleteFile(key string) (err error) {
  124. switch a.OssType {
  125. case OssTypeLocal:
  126. err = a.deleteFile(key)
  127. return
  128. case OssTypeAli:
  129. engine := new(AwsS3)
  130. byteData, _ := json.Marshal(a.OriginConf)
  131. err = json.Unmarshal(byteData, &engine)
  132. if err != nil {
  133. return
  134. }
  135. err = engine.DeleteFile(key)
  136. return
  137. case OssTypeAws:
  138. engine := new(AliOSS)
  139. byteData, _ := json.Marshal(a.OriginConf)
  140. err = json.Unmarshal(byteData, &engine)
  141. if err != nil {
  142. return
  143. }
  144. err = engine.DeleteFile(key)
  145. return
  146. default:
  147. err = fmt.Errorf("oss type not support")
  148. break
  149. }
  150. return
  151. }
  152. func (a *File) uploadFile(file *multipart.FileHeader) (viewPath, filename string, err error) {
  153. // 读取文件后缀
  154. ext := filepath.Ext(file.Filename)
  155. // 读取文件名并md5
  156. name := strings.TrimSuffix(file.Filename, ext)
  157. name = utils.Md5ByteEncode([]byte(name))
  158. // 拼接新文件名
  159. filename = name + "_" + time.Now().Format("20060102150405") + ext
  160. // 尝试创建此路径
  161. mkdirErr := os.MkdirAll(a.StorePath, os.ModePerm)
  162. if mkdirErr != nil {
  163. return "", "", errors.New("function os.MkdirAll() failed, err:" + mkdirErr.Error())
  164. }
  165. // 拼接路径和文件名
  166. p := utils.ToPath(a.StorePath, filename) // 存储文件
  167. viewPath = utils.ToPath(a.Path, a.ProxyPath, filename)
  168. f, openError := file.Open() // 读取文件
  169. if openError != nil {
  170. return "", "", errors.New("function file.Open() failed, err:" + openError.Error())
  171. }
  172. defer func() {
  173. _ = f.Close()
  174. }() // 创建文件 defer 关闭
  175. out, createErr := os.Create(p)
  176. if createErr != nil {
  177. return "", "", errors.New("function os.Create() failed, err:" + createErr.Error())
  178. }
  179. defer func() {
  180. _ = out.Close()
  181. }()
  182. // 创建文件 defer 关闭
  183. _, copyErr := io.Copy(out, f) // 传输(拷贝)文件
  184. if copyErr != nil {
  185. return "", "", errors.New("function io.Copy() failed, err:" + copyErr.Error())
  186. }
  187. return
  188. }
  189. var mu sync.Mutex
  190. func (a *File) deleteFile(key string) (err error) {
  191. // 检查 key 是否为空
  192. if key == "" {
  193. return errors.New("key不能为空")
  194. }
  195. // 验证 key 是否包含非法字符或尝试访问存储路径之外的文件
  196. if strings.Contains(key, "..") || strings.ContainsAny(key, `\/:*?"<>|`) {
  197. return errors.New("非法的key")
  198. }
  199. p := filepath.Join(a.StorePath, key)
  200. // 检查文件是否存在
  201. if _, err := os.Stat(p); os.IsNotExist(err) {
  202. return errors.New("文件不存在")
  203. }
  204. // 使用文件锁防止并发删除
  205. mu.Lock()
  206. defer mu.Unlock()
  207. err = os.Remove(p)
  208. if err != nil {
  209. return errors.New("文件删除失败: " + err.Error())
  210. }
  211. return
  212. }
  213. const (
  214. FileJobRunning = 1 // 进行中
  215. FileJobFinish = 2 // 已完成
  216. FileJobFail = 3 // 失败
  217. OssTypeLocal = "local"
  218. OssTypeAli = "ali"
  219. OssTypeAws = "aws"
  220. )
  221. type OriginFile interface {
  222. UploadFile(*multipart.FileHeader) (string, string, error)
  223. DeleteFile(key string) error
  224. }
  225. type AliOSS struct {
  226. Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
  227. AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
  228. AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
  229. BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
  230. BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
  231. BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
  232. }
  233. func (a *AliOSS) UploadFile(file *multipart.FileHeader) (string, string, error) {
  234. bucket, err := a.aliBucket()
  235. if err != nil {
  236. return "", "", errors.New("aliBucket Failed, err:" + err.Error())
  237. }
  238. // 读取本地文件。
  239. f, openError := file.Open()
  240. if openError != nil {
  241. return "", "", errors.New("function file.Open() Failed, err:" + openError.Error())
  242. }
  243. defer func() {
  244. _ = f.Close()
  245. }() // 创建文件 defer 关闭
  246. // 上传阿里云路径 文件名格式 自己可以改 建议保证唯一性
  247. yunFileTmpPath := a.BasePath + "/" + "uploads" + "/" + time.Now().Format("2006-01-02") + "/" + file.Filename
  248. // 上传文件流。
  249. err = bucket.PutObject(yunFileTmpPath, f)
  250. if err != nil {
  251. return "", "", errors.New("PutObject Failed, err:" + err.Error())
  252. }
  253. return a.BucketUrl + "/" + yunFileTmpPath, yunFileTmpPath, nil
  254. }
  255. func (a *AliOSS) DeleteFile(key string) error {
  256. bucket, err := a.aliBucket()
  257. if err != nil {
  258. return errors.New("aliBucket err:" + err.Error())
  259. }
  260. // 删除单个文件。objectName表示删除OSS文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
  261. // 如需删除文件夹,请将objectName设置为对应的文件夹名称。如果文件夹非空,则需要将文件夹下的所有object删除后才能删除该文件夹。
  262. err = bucket.DeleteObject(key)
  263. if err != nil {
  264. return errors.New("bucket.DeleteObject, err:" + err.Error())
  265. }
  266. return nil
  267. }
  268. func (a *AliOSS) aliBucket() (*oss.Bucket, error) {
  269. var err error
  270. var client *oss.Client
  271. var buket *oss.Bucket
  272. // 创建OSSClient实例。
  273. client, err = oss.New(a.Endpoint, a.AccessKeyId, a.AccessKeySecret)
  274. if err != nil {
  275. return nil, err
  276. }
  277. // 获取存储空间。
  278. buket, err = client.Bucket(a.BucketName)
  279. if err != nil {
  280. return nil, err
  281. }
  282. return buket, err
  283. }
  284. type AwsS3 struct {
  285. Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
  286. Region string `mapstructure:"region" json:"region" yaml:"region"`
  287. Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
  288. SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
  289. SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
  290. BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
  291. PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
  292. S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"`
  293. DisableSSL bool `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"`
  294. _session *session.Session
  295. }
  296. func (a *AwsS3) UploadFile(file *multipart.FileHeader) (string, string, error) {
  297. _session := a.newSession()
  298. uploader := s3manager.NewUploader(_session)
  299. fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename)
  300. filename := a.PathPrefix + "/" + fileKey
  301. f, openError := file.Open()
  302. if openError != nil {
  303. return "", "", errors.New("function file.Open() failed, err:" + openError.Error())
  304. }
  305. defer func() {
  306. _ = f.Close()
  307. }() // 创建文件 defer 关闭
  308. _, err := uploader.Upload(&s3manager.UploadInput{
  309. Bucket: aws.String(a.Bucket),
  310. Key: aws.String(filename),
  311. Body: f,
  312. })
  313. if err != nil {
  314. return "", "", err
  315. }
  316. return a.BaseURL + "/" + filename, fileKey, nil
  317. }
  318. func (a *AwsS3) DeleteFile(key string) error {
  319. _session := a.newSession()
  320. svc := s3.New(_session)
  321. filename := a.PathPrefix + "/" + key
  322. bucket := a.Bucket
  323. _, err := svc.DeleteObject(&s3.DeleteObjectInput{
  324. Bucket: aws.String(bucket),
  325. Key: aws.String(filename),
  326. })
  327. if err != nil {
  328. return errors.New("function svc.DeleteObject() failed, err:" + err.Error())
  329. }
  330. _ = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
  331. Bucket: aws.String(bucket),
  332. Key: aws.String(filename),
  333. })
  334. return nil
  335. }
  336. // newSession Create S3 session
  337. func (a *AwsS3) newSession() *session.Session {
  338. sess, _ := session.NewSession(&aws.Config{
  339. Region: aws.String(a.Region),
  340. Endpoint: aws.String(a.Endpoint), //minio在这里设置地址,可以兼容
  341. S3ForcePathStyle: aws.Bool(a.S3ForcePathStyle),
  342. DisableSSL: aws.Bool(a.DisableSSL),
  343. Credentials: credentials.NewStaticCredentials(
  344. a.SecretID,
  345. a.SecretKey,
  346. "",
  347. ),
  348. })
  349. return sess
  350. }