# 存储工具文档 ## 概述 存储工具提供了文件上传和查看功能,支持OSS和MinIO两种存储方式,并提供HTTP处理器用于文件上传和代理查看。 ## 功能特性 - 支持OSS对象存储(阿里云、腾讯云、AWS、七牛云等) - 支持MinIO对象存储 - 提供统一的存储接口 - 支持文件上传HTTP处理器 - 支持文件代理查看HTTP处理器 - 支持文件大小和扩展名限制 - 自动生成唯一文件名 - 支持自定义对象键前缀 ## 使用方法 ### 1. 创建存储实例 ```go import ( "github.com/go-common/config" "github.com/go-common/storage" ) // 加载配置 cfg, err := config.LoadFromFile("./config.json") if err != nil { log.Fatal(err) } // 创建OSS存储实例 ossStorage, err := storage.NewStorage(storage.StorageTypeOSS, cfg) if err != nil { log.Fatal(err) } // 创建MinIO存储实例 minioStorage, err := storage.NewStorage(storage.StorageTypeMinIO, cfg) if err != nil { log.Fatal(err) } ``` ### 2. 上传文件 ```go import ( "context" "os" "github.com/go-common/storage" ) // 打开文件 file, err := os.Open("test.jpg") if err != nil { log.Fatal(err) } defer file.Close() // 上传文件 ctx := context.Background() objectKey := "images/test.jpg" err = ossStorage.Upload(ctx, objectKey, file, "image/jpeg") if err != nil { log.Fatal(err) } // 获取文件URL url, err := ossStorage.GetURL(objectKey, 0) if err != nil { log.Fatal(err) } fmt.Printf("File URL: %s\n", url) ``` ### 3. 使用HTTP处理器上传文件 ```go import ( "net/http" "github.com/go-common/storage" ) // 创建上传处理器 uploadHandler := storage.NewUploadHandler(storage.UploadHandlerConfig{ Storage: ossStorage, MaxFileSize: 10 * 1024 * 1024, // 10MB AllowedExts: []string{".jpg", ".jpeg", ".png", ".gif"}, ObjectPrefix: "images/", }) // 注册路由 http.Handle("/upload", uploadHandler) http.ListenAndServe(":8080", nil) ``` **上传请求示例:** ```bash curl -X POST http://localhost:8080/upload \ -F "file=@test.jpg" \ -F "prefix=images/" ``` **响应示例:** ```json { "code": 0, "message": "Upload successful", "timestamp": 1704067200, "data": { "objectKey": "images/test_1704067200000000000.jpg", "url": "https://bucket.oss-cn-hangzhou.aliyuncs.com/images/test_1704067200000000000.jpg", "size": 102400, "contentType": "image/jpeg", "uploadTime": "2024-01-01T12:00:00Z" } } ``` ### 4. 使用HTTP处理器查看文件 ```go import ( "net/http" "github.com/go-common/storage" ) // 创建代理查看处理器 proxyHandler := storage.NewProxyHandler(ossStorage) // 注册路由 http.Handle("/file", proxyHandler) http.ListenAndServe(":8080", nil) ``` **查看请求示例:** ``` GET /file?key=images/test.jpg ``` ### 5. 生成对象键 ```go import "github.com/go-common/storage" // 生成简单对象键 objectKey := storage.GenerateObjectKey("images/", "test.jpg") // 输出: "images/test.jpg" // 生成带日期的对象键 objectKey := storage.GenerateObjectKeyWithDate("images", "test.jpg") // 输出: "images/2024/01/01/test.jpg" ``` ### 6. 删除文件 ```go ctx := context.Background() err := ossStorage.Delete(ctx, "images/test.jpg") if err != nil { log.Fatal(err) } ``` ### 7. 检查文件是否存在 ```go ctx := context.Background() exists, err := ossStorage.Exists(ctx, "images/test.jpg") if err != nil { log.Fatal(err) } if exists { fmt.Println("File exists") } ``` ## API 参考 ### Storage 接口 ```go type Storage interface { // Upload 上传文件 Upload(ctx context.Context, objectKey string, reader io.Reader, contentType ...string) error // GetURL 获取文件访问URL GetURL(objectKey string, expires int64) (string, error) // Delete 删除文件 Delete(ctx context.Context, objectKey string) error // Exists 检查文件是否存在 Exists(ctx context.Context, objectKey string) (bool, error) // GetObject 获取文件内容 GetObject(ctx context.Context, objectKey string) (io.ReadCloser, error) } ``` ### NewStorage(storageType StorageType, cfg *config.Config) (Storage, error) 创建存储实例。 **参数:** - `storageType`: 存储类型(`storage.StorageTypeOSS` 或 `storage.StorageTypeMinIO`) - `cfg`: 配置对象 **返回:** 存储实例和错误信息 ### UploadHandler 文件上传HTTP处理器。 #### NewUploadHandler(cfg UploadHandlerConfig) *UploadHandler 创建上传处理器。 **配置参数:** - `Storage`: 存储实例 - `MaxFileSize`: 最大文件大小(字节),0表示不限制 - `AllowedExts`: 允许的文件扩展名,空表示不限制 - `ObjectPrefix`: 对象键前缀 #### 请求格式 - **方法**: POST - **表单字段**: - `file`: 文件(必需) - `prefix`: 对象键前缀(可选,会覆盖配置中的前缀) #### 响应格式 ```json { "code": 0, "message": "Upload successful", "timestamp": 1704067200, "data": { "objectKey": "images/test.jpg", "url": "https://...", "size": 102400, "contentType": "image/jpeg", "uploadTime": "2024-01-01T12:00:00Z" } } ``` ### ProxyHandler 文件代理查看HTTP处理器。 #### NewProxyHandler(storage Storage) *ProxyHandler 创建代理查看处理器。 #### 请求格式 - **方法**: GET - **URL参数**: - `key`: 对象键(必需) #### 响应 直接返回文件内容,设置适当的Content-Type。 ### 辅助函数 #### GenerateObjectKey(prefix, filename string) string 生成对象键。 #### GenerateObjectKeyWithDate(prefix, filename string) string 生成带日期的对象键(格式: prefix/YYYY/MM/DD/filename)。 ## 完整示例 ### 示例1:文件上传和查看 ```go package main import ( "log" "net/http" "github.com/go-common/config" "github.com/go-common/middleware" "github.com/go-common/storage" ) func main() { // 加载配置 cfg, err := config.LoadFromFile("./config.json") if err != nil { log.Fatal(err) } // 创建存储实例(使用OSS) ossStorage, err := storage.NewStorage(storage.StorageTypeOSS, cfg) if err != nil { log.Fatal(err) } // 创建上传处理器 uploadHandler := storage.NewUploadHandler(storage.UploadHandlerConfig{ Storage: ossStorage, MaxFileSize: 10 * 1024 * 1024, // 10MB AllowedExts: []string{".jpg", ".jpeg", ".png", ".gif", ".pdf"}, ObjectPrefix: "uploads/", }) // 创建代理查看处理器 proxyHandler := storage.NewProxyHandler(ossStorage) // 创建中间件链 chain := middleware.NewChain( middleware.CORS(cfg.GetCORS()), middleware.Timezone, ) // 注册路由 mux := http.NewServeMux() mux.Handle("/upload", chain.Then(uploadHandler)) mux.Handle("/file", chain.Then(proxyHandler)) log.Println("Server started on :8080") log.Fatal(http.ListenAndServe(":8080", mux)) } ``` ### 示例2:直接使用存储接口 ```go package main import ( "context" "fmt" "log" "os" "github.com/go-common/config" "github.com/go-common/storage" ) func main() { // 加载配置 cfg, err := config.LoadFromFile("./config.json") if err != nil { log.Fatal(err) } // 创建存储实例 s, err := storage.NewStorage(storage.StorageTypeMinIO, cfg) if err != nil { log.Fatal(err) } // 打开文件 file, err := os.Open("test.jpg") if err != nil { log.Fatal(err) } defer file.Close() // 生成对象键 objectKey := storage.GenerateObjectKeyWithDate("images", "test.jpg") // 上传文件 ctx := context.Background() err = s.Upload(ctx, objectKey, file, "image/jpeg") if err != nil { log.Fatal(err) } // 获取文件URL url, err := s.GetURL(objectKey, 0) if err != nil { log.Fatal(err) } fmt.Printf("File uploaded: %s\n", url) } ``` ## 注意事项 1. **OSS和MinIO SDK实现**: - 当前实现提供了接口和框架,但具体的OSS和MinIO SDK集成需要根据实际使用的SDK实现 - 需要在`oss.go`和`minio.go`中实现具体的SDK调用 2. **文件大小限制**: - 建议设置合理的文件大小限制 - 大文件上传可能需要分片上传 3. **文件扩展名验证**: - 建议限制允许的文件类型,防止上传恶意文件 - 仅验证扩展名不够安全,建议结合文件内容验证 4. **安全性**: - 上传接口应该添加身份验证 - 代理查看接口可以添加访问控制 5. **性能优化**: - 对于大文件,考虑使用分片上传 - 代理查看可以添加缓存机制 6. **错误处理**: - 所有操作都应该进行错误处理 - 建议记录详细的错误日志 ## 实现OSS和MinIO SDK集成 由于不同的OSS提供商和MinIO有不同的SDK,当前实现提供了框架,需要根据实际情况集成: ### OSS SDK集成示例(阿里云OSS) ```go import ( "github.com/aliyun/aliyun-oss-go-sdk/oss" ) func NewOSSStorage(cfg *config.OSSConfig) (*OSSStorage, error) { client, err := oss.New(cfg.Endpoint, cfg.AccessKeyID, cfg.AccessKeySecret) if err != nil { return nil, err } storage := &OSSStorage{ config: cfg, client: client, } return storage, nil } func (s *OSSStorage) Upload(ctx context.Context, objectKey string, reader io.Reader, contentType ...string) error { bucket, err := s.client.Bucket(s.config.Bucket) if err != nil { return err } options := []oss.Option{} if len(contentType) > 0 && contentType[0] != "" { options = append(options, oss.ContentType(contentType[0])) } return bucket.PutObject(objectKey, reader, options...) } ``` ### MinIO SDK集成示例 ```go import ( "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" ) func NewMinIOStorage(cfg *config.MinIOConfig) (*MinIOStorage, error) { client, err := minio.New(cfg.Endpoint, &minio.Options{ Creds: credentials.NewStaticV4(cfg.AccessKeyID, cfg.SecretAccessKey, ""), Secure: cfg.UseSSL, }) if err != nil { return nil, err } storage := &MinIOStorage{ config: cfg, client: client, } return storage, nil } func (s *MinIOStorage) Upload(ctx context.Context, objectKey string, reader io.Reader, contentType ...string) error { ct := "application/octet-stream" if len(contentType) > 0 && contentType[0] != "" { ct = contentType[0] } _, err := s.client.PutObject(ctx, s.config.Bucket, objectKey, reader, -1, minio.PutObjectOptions{ ContentType: ct, }) return err } ``` ## 示例 完整示例请参考 `examples/storage_example.go`