初始版本,工具基础类
This commit is contained in:
496
docs/storage.md
Normal file
496
docs/storage.md
Normal file
@@ -0,0 +1,496 @@
|
||||
# 存储工具文档
|
||||
|
||||
## 概述
|
||||
|
||||
存储工具提供了文件上传和查看功能,支持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`
|
||||
|
||||
Reference in New Issue
Block a user