package three_party import ( "errors" "fmt" uuid "github.com/iris-contrib/go.uuid" "github.com/kataras/iris/v12" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "io" "log" "mime/multipart" "os" "path/filepath" "strings" "sync" "time" ) var defaultFileBucket = "file" var defaultIconBucket = "icon" var defaultLocation = "us-east-1" var minioServerPool *sync.Pool // client version and secret, it should be from client side const ( UploadDir string = "./public/upload/" UrlBase string = "/upload/" ) // DstFile 文件 type DstFile struct { Name string `json:"name"` Src string `json:"src"` } func init() { minioServerPool = &sync.Pool{ New: func() interface{}{ return &MinioServe{} }, } } type MinioFileObject struct { BucketName string ObjectName string FilePath string ContentType string Config MinioConfig } type MinioConfig struct { MinioHost string `json:"minioHost"` MinioPort int64 `json:"minioPort"` AccessKey string `json:"accessKey"` SecretKey string `json:"secretKey"` UseSSL bool `json:"useSSL"` } func (c *MinioConfig) Validate() error { if c.MinioHost == "" { return errors.New("minio host not specified") } if c.MinioPort <= 0 { return errors.New("minio port not specified") } if c.AccessKey == "" { return errors.New("minio access key is not specified") } if c.SecretKey == "" { return errors.New("minio secret key is not specified") } return nil } func (c *MinioConfig) GetClient()(*minio.Client, error) { if err := c.Validate(); err != nil { return nil, err } endPoint := fmt.Sprintf("%s:%d", c.MinioHost, c.MinioPort) minioClient, err := minio.New( endPoint, &minio.Options{ Creds: credentials.NewStaticV4(c.AccessKey, c.SecretKey, ""), Secure: c.UseSSL, }) if err != nil { log.Fatalf("connect minio server fail %s url %s", err.Error(), endPoint) } return minioClient, nil } type MinioServerOption func(*MinioServe) type MinioServe struct { Client *minio.Client } func NewMinioServer(opts ...MinioServerOption)(*MinioServe, error){ m := &MinioServe{} for _, opt := range opts { opt(m) } return m, nil } func (m *MinioServe) Reset() { m.Client = nil } func (m *MinioServe) checkBucket(ctx iris.Context, bucketName string) bool { isExist, err1 := m.Client.BucketExists(ctx, bucketName) if err1 != nil { log.Fatalf("checkBucket error " + err1.Error()) return false } if !isExist { err2 := m.Client.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: defaultLocation}) if err2 != nil { log.Fatalf("checkBucket error " + err2.Error()) return false } } return true } func (m *MinioServe) CheckBuckets(ctx iris.Context) bool { b1 := m.checkBucket(ctx, defaultFileBucket) b2 := m.checkBucket(ctx, defaultIconBucket) return b1&&b2 } func (m *MinioServe) UploadFile(ctx iris.Context, obj *MinioFileObject)(*minio.UploadInfo, error) { bBucketExist := m.CheckBuckets(ctx) if !bBucketExist { return nil, nil } info, err := m.Client.FPutObject(ctx, obj.BucketName, obj.ObjectName, obj.FilePath, minio.PutObjectOptions{ ContentType: obj.ContentType, }) if err != nil { return &info, err } return &info, nil } func acquireMinioServer() *MinioServe { return minioServerPool.Get().(*MinioServe) } func releaseMinioServer(m *MinioServe) { m.Reset() minioServerPool.Put(m) } func UploadFile(ctx iris.Context) (*DstFile,error){ file ,info ,err := ctx.FormFile("uploadfile") if err != nil { return nil ,err } defer file.Close() oldFileName := info.Filename destFile, url := GenerateNewFileName(oldFileName) out, err := os.OpenFile(destFile, os.O_WRONLY|os.O_CREATE, 0666) if err != nil{ return nil, err } defer out.Close() _ ,err = io.Copy(out, file) if err != nil { return nil, err } outfile := &DstFile{Name: oldFileName ,Src:url } return outfile, nil } // UploadMultiFiles Upload Multi Files func UploadMultiFiles(ctx iris.Context)(int, []DstFile, error){ maxSize := ctx.Application().ConfigurationReadOnly().GetPostMaxMemory() err := ctx.Request().ParseMultipartForm(maxSize) if err != nil { return 0 , nil, err } form := ctx.Request().MultipartForm files := form.File["files[]"] count := len(files) failures := 0 dstIndex := 1 outFiles := []DstFile{} for _, file := range files { url, err := SaveUploadFile(file) if err != nil { failures++ }else{ out := DstFile{Name: file.Filename ,Src:url} outFiles = append(outFiles, out) dstIndex++ } } num := count - failures return num ,outFiles ,nil } func SaveUploadFile(fh *multipart.FileHeader)(string, error){ src ,err := fh.Open() if err != nil { return "" ,err } defer src.Close() oldFileName := fh.Filename srcFile, urlFile := GenerateNewFileName(oldFileName) out, err := os.OpenFile(srcFile ,os.O_WRONLY|os.O_CREATE ,os.FileMode(0666)) if err != nil { return "" ,err } defer out.Close() _, err = io.Copy(out, src) return urlFile, err } // CreateDateDir Create date directory func CreateDateDir() (srcPath, urlPath string) { folderName := time.Now().Format("20060102") src := filepath.Join(UploadDir, folderName) url := filepath.Join(UrlBase, folderName) _, err := os.Stat(srcPath) if os.IsNotExist(err ){ _ = os.Mkdir(src, os.ModePerm) _ = os.Chmod(src, os.ModePerm) } return src ,url } // GenerateNewFileName Generate new file name func GenerateNewFileName(oldFileName string) (srcPath, urlPath string) { u1, _ := uuid.NewV4() newFileName := u1.String() src , url := CreateDateDir() dotIndex := strings.LastIndex(oldFileName, ".") if dotIndex != -1 && dotIndex != 0 { newFileName += oldFileName[dotIndex:] } src = filepath.Join(src, newFileName) url = filepath.Join(url, newFileName) return src, url }