| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- package config
- import (
- "encoding/csv"
- "encoding/json"
- "fmt"
- "github.com/aliyun/aliyun-oss-go-sdk/oss"
- "github.com/aws/aws-sdk-go/aws"
- "github.com/aws/aws-sdk-go/aws/credentials"
- "github.com/aws/aws-sdk-go/aws/session"
- "github.com/aws/aws-sdk-go/service/s3"
- "github.com/aws/aws-sdk-go/service/s3/s3manager"
- "github.com/pkg/errors"
- "github.com/xuri/excelize/v2"
- "go_server/utils"
- "io"
- "mime/multipart"
- "os"
- "path/filepath"
- "reflect"
- "strings"
- "sync"
- "time"
- )
- type File struct {
- OssType string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"` // 文件存储类型
- Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件访问路径
- ProxyPath string `mapstructure:"proxy-path" json:"proxy-path" yaml:"proxy-path"` // 系统代理路径
- StorePath string `mapstructure:"store-path" json:"store-path" yaml:"store-path"` // 本地文件存储路径
- OriginConf map[string]interface{} `mapstructure:"origin-conf" json:"origin-conf" yaml:"origin-conf"` // 远程文件存储配置
- }
- // 导出csv表
- func ExportCsv(headers []string, data [][]string, filename string) (string, error) {
- if filename == "" || len(data) == 0 {
- return "", fmt.Errorf("数据错误")
- }
- // exportCSV 导出CSV文件
- // 生成文件名,包含当前时间戳
- // 创建文件
- file, err := os.Create(filename)
- if err != nil {
- return "", fmt.Errorf("创建文件失败: %v", err)
- }
- defer file.Close()
- file.WriteString("\xEF\xBB\xBF")
- // 创建CSV writer
- writer := csv.NewWriter(file)
- defer writer.Flush()
- // 写入表头
- if err := writer.Write(headers); err != nil {
- return "", fmt.Errorf("写入表头失败: %v", err)
- }
- // 写入数据
- for _, record := range data {
- if err := writer.Write(record); err != nil {
- return "", fmt.Errorf("写入数据失败: %v", err)
- }
- }
- return filename, nil
- }
- func ExportExcelFile[T any](headers []string, data []T, savePath string) error {
- if savePath == "" || len(data) == 0 {
- return fmt.Errorf("无目标数据")
- }
- f := excelize.NewFile()
- sheetName := "Sheet1"
- if err := f.SetSheetName("Sheet1", sheetName); err != nil {
- return err
- }
- // 写入表头
- for i, header := range headers {
- cell, _ := excelize.CoordinatesToCellName(i+1, 1)
- if err := f.SetCellValue(sheetName, cell, header); err != nil {
- return err
- }
- }
- // 使用反射获取结构体字段值
- for rowIndex, row := range data {
- rowValue := reflect.ValueOf(row)
- for colIndex := 0; colIndex < rowValue.NumField(); colIndex++ {
- cell, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+2)
- field := rowValue.Field(colIndex)
- if field.CanInterface() {
- if err := f.SetCellValue(sheetName, cell, field.Interface()); err != nil {
- return err
- }
- }
- }
- }
- // 保存 Excel 文件
- if err := f.SaveAs(savePath); err != nil {
- return err
- }
- return nil
- }
- func (a *File) UploadFile(file *multipart.FileHeader) (viewPath, savePath string, err error) {
- switch a.OssType {
- case OssTypeLocal:
- viewPath, savePath, err = a.uploadFile(file)
- return
- case OssTypeAws:
- engine := new(AwsS3)
- byteData, _ := json.Marshal(a.OriginConf)
- err = json.Unmarshal(byteData, &engine)
- if err != nil {
- return
- }
- viewPath, savePath, err = engine.UploadFile(file)
- case OssTypeAli:
- engine := new(AliOSS)
- byteData, _ := json.Marshal(a.OriginConf)
- err = json.Unmarshal(byteData, &engine)
- if err != nil {
- return
- }
- viewPath, savePath, err = engine.UploadFile(file)
- return
- default:
- err = fmt.Errorf("oss type not support")
- break
- }
- return
- }
- func (a *File) DeleteFile(key string) (err error) {
- switch a.OssType {
- case OssTypeLocal:
- err = a.deleteFile(key)
- return
- case OssTypeAli:
- engine := new(AwsS3)
- byteData, _ := json.Marshal(a.OriginConf)
- err = json.Unmarshal(byteData, &engine)
- if err != nil {
- return
- }
- err = engine.DeleteFile(key)
- return
- case OssTypeAws:
- engine := new(AliOSS)
- byteData, _ := json.Marshal(a.OriginConf)
- err = json.Unmarshal(byteData, &engine)
- if err != nil {
- return
- }
- err = engine.DeleteFile(key)
- return
- default:
- err = fmt.Errorf("oss type not support")
- break
- }
- return
- }
- func (a *File) uploadFile(file *multipart.FileHeader) (viewPath, filename string, err error) {
- // 读取文件后缀
- ext := filepath.Ext(file.Filename)
- // 读取文件名并md5
- name := strings.TrimSuffix(file.Filename, ext)
- name = utils.Md5ByteEncode([]byte(name))
- // 拼接新文件名
- filename = name + "_" + time.Now().Format("20060102150405") + ext
- // 尝试创建此路径
- mkdirErr := os.MkdirAll(a.StorePath, os.ModePerm)
- if mkdirErr != nil {
- return "", "", errors.New("function os.MkdirAll() failed, err:" + mkdirErr.Error())
- }
- // 拼接路径和文件名
- p := utils.ToPath(a.StorePath, filename) // 存储文件
- viewPath = utils.ToPath(a.Path, a.ProxyPath, filename)
- f, openError := file.Open() // 读取文件
- if openError != nil {
- return "", "", errors.New("function file.Open() failed, err:" + openError.Error())
- }
- defer func() {
- _ = f.Close()
- }() // 创建文件 defer 关闭
- out, createErr := os.Create(p)
- if createErr != nil {
- return "", "", errors.New("function os.Create() failed, err:" + createErr.Error())
- }
- defer func() {
- _ = out.Close()
- }()
- // 创建文件 defer 关闭
- _, copyErr := io.Copy(out, f) // 传输(拷贝)文件
- if copyErr != nil {
- return "", "", errors.New("function io.Copy() failed, err:" + copyErr.Error())
- }
- return
- }
- var mu sync.Mutex
- func (a *File) deleteFile(key string) (err error) {
- // 检查 key 是否为空
- if key == "" {
- return errors.New("key不能为空")
- }
- // 验证 key 是否包含非法字符或尝试访问存储路径之外的文件
- if strings.Contains(key, "..") || strings.ContainsAny(key, `\/:*?"<>|`) {
- return errors.New("非法的key")
- }
- p := filepath.Join(a.StorePath, key)
- // 检查文件是否存在
- if _, err := os.Stat(p); os.IsNotExist(err) {
- return errors.New("文件不存在")
- }
- // 使用文件锁防止并发删除
- mu.Lock()
- defer mu.Unlock()
- err = os.Remove(p)
- if err != nil {
- return errors.New("文件删除失败: " + err.Error())
- }
- return
- }
- const (
- FileJobRunning = 1 // 进行中
- FileJobFinish = 2 // 已完成
- FileJobFail = 3 // 失败
- OssTypeLocal = "local"
- OssTypeAli = "ali"
- OssTypeAws = "aws"
- )
- type OriginFile interface {
- UploadFile(*multipart.FileHeader) (string, string, error)
- DeleteFile(key string) error
- }
- type AliOSS struct {
- Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
- AccessKeyId string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
- AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
- BucketName string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
- BucketUrl string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
- BasePath string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
- }
- func (a *AliOSS) UploadFile(file *multipart.FileHeader) (string, string, error) {
- bucket, err := a.aliBucket()
- if err != nil {
- return "", "", errors.New("aliBucket Failed, err:" + err.Error())
- }
- // 读取本地文件。
- f, openError := file.Open()
- if openError != nil {
- return "", "", errors.New("function file.Open() Failed, err:" + openError.Error())
- }
- defer func() {
- _ = f.Close()
- }() // 创建文件 defer 关闭
- // 上传阿里云路径 文件名格式 自己可以改 建议保证唯一性
- yunFileTmpPath := a.BasePath + "/" + "uploads" + "/" + time.Now().Format("2006-01-02") + "/" + file.Filename
- // 上传文件流。
- err = bucket.PutObject(yunFileTmpPath, f)
- if err != nil {
- return "", "", errors.New("PutObject Failed, err:" + err.Error())
- }
- return a.BucketUrl + "/" + yunFileTmpPath, yunFileTmpPath, nil
- }
- func (a *AliOSS) DeleteFile(key string) error {
- bucket, err := a.aliBucket()
- if err != nil {
- return errors.New("aliBucket err:" + err.Error())
- }
- // 删除单个文件。objectName表示删除OSS文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
- // 如需删除文件夹,请将objectName设置为对应的文件夹名称。如果文件夹非空,则需要将文件夹下的所有object删除后才能删除该文件夹。
- err = bucket.DeleteObject(key)
- if err != nil {
- return errors.New("bucket.DeleteObject, err:" + err.Error())
- }
- return nil
- }
- func (a *AliOSS) aliBucket() (*oss.Bucket, error) {
- var err error
- var client *oss.Client
- var buket *oss.Bucket
- // 创建OSSClient实例。
- client, err = oss.New(a.Endpoint, a.AccessKeyId, a.AccessKeySecret)
- if err != nil {
- return nil, err
- }
- // 获取存储空间。
- buket, err = client.Bucket(a.BucketName)
- if err != nil {
- return nil, err
- }
- return buket, err
- }
- type AwsS3 struct {
- Bucket string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
- Region string `mapstructure:"region" json:"region" yaml:"region"`
- Endpoint string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
- SecretID string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
- SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
- BaseURL string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
- PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
- S3ForcePathStyle bool `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"`
- DisableSSL bool `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"`
- _session *session.Session
- }
- func (a *AwsS3) UploadFile(file *multipart.FileHeader) (string, string, error) {
- _session := a.newSession()
- uploader := s3manager.NewUploader(_session)
- fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename)
- filename := a.PathPrefix + "/" + fileKey
- f, openError := file.Open()
- if openError != nil {
- return "", "", errors.New("function file.Open() failed, err:" + openError.Error())
- }
- defer func() {
- _ = f.Close()
- }() // 创建文件 defer 关闭
- _, err := uploader.Upload(&s3manager.UploadInput{
- Bucket: aws.String(a.Bucket),
- Key: aws.String(filename),
- Body: f,
- })
- if err != nil {
- return "", "", err
- }
- return a.BaseURL + "/" + filename, fileKey, nil
- }
- func (a *AwsS3) DeleteFile(key string) error {
- _session := a.newSession()
- svc := s3.New(_session)
- filename := a.PathPrefix + "/" + key
- bucket := a.Bucket
- _, err := svc.DeleteObject(&s3.DeleteObjectInput{
- Bucket: aws.String(bucket),
- Key: aws.String(filename),
- })
- if err != nil {
- return errors.New("function svc.DeleteObject() failed, err:" + err.Error())
- }
- _ = svc.WaitUntilObjectNotExists(&s3.HeadObjectInput{
- Bucket: aws.String(bucket),
- Key: aws.String(filename),
- })
- return nil
- }
- // newSession Create S3 session
- func (a *AwsS3) newSession() *session.Session {
- sess, _ := session.NewSession(&aws.Config{
- Region: aws.String(a.Region),
- Endpoint: aws.String(a.Endpoint), //minio在这里设置地址,可以兼容
- S3ForcePathStyle: aws.Bool(a.S3ForcePathStyle),
- DisableSSL: aws.Bool(a.DisableSSL),
- Credentials: credentials.NewStaticCredentials(
- a.SecretID,
- a.SecretKey,
- "",
- ),
- })
- return sess
- }
|