11 KiB
11 KiB
存储工具文档
概述
存储工具提供了文件上传和查看功能,支持OSS和MinIO两种存储方式,并提供HTTP处理器用于文件上传和代理查看。
功能特性
- 支持OSS对象存储(阿里云、腾讯云、AWS、七牛云等)
- 支持MinIO对象存储
- 提供统一的存储接口
- 支持文件上传HTTP处理器
- 支持文件代理查看HTTP处理器
- 支持文件大小和扩展名限制
- 自动生成唯一文件名
- 支持自定义对象键前缀
使用方法
1. 创建存储实例
import (
"git.toowon.com/jimmy/go-commom/config"
"git.toowon.com/jimmy/go-commom/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. 上传文件
import (
"context"
"os"
"git.toowon.com/jimmy/go-commom/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处理器上传文件
import (
"net/http"
"git.toowon.com/jimmy/go-commom/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)
上传请求示例:
curl -X POST http://localhost:8080/upload \
-F "file=@test.jpg" \
-F "prefix=images/"
响应示例:
{
"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处理器查看文件
import (
"net/http"
"git.toowon.com/jimmy/go-commom/storage"
)
// 创建代理查看处理器
proxyHandler := storage.NewProxyHandler(ossStorage)
// 注册路由
http.Handle("/file", proxyHandler)
http.ListenAndServe(":8080", nil)
查看请求示例:
GET /file?key=images/test.jpg
5. 生成对象键
import "git.toowon.com/jimmy/go-commom/storage"
// 生成简单对象键
objectKey := storage.GenerateObjectKey("images/", "test.jpg")
// 输出: "images/test.jpg"
// 生成带日期的对象键
objectKey := storage.GenerateObjectKeyWithDate("images", "test.jpg")
// 输出: "images/2024/01/01/test.jpg"
6. 删除文件
ctx := context.Background()
err := ossStorage.Delete(ctx, "images/test.jpg")
if err != nil {
log.Fatal(err)
}
7. 检查文件是否存在
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 接口
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: 对象键前缀(可选,会覆盖配置中的前缀)
响应格式
{
"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:文件上传和查看
package main
import (
"log"
"net/http"
"git.toowon.com/jimmy/go-commom/config"
"git.toowon.com/jimmy/go-commom/middleware"
"git.toowon.com/jimmy/go-commom/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:直接使用存储接口
package main
import (
"context"
"fmt"
"log"
"os"
"git.toowon.com/jimmy/go-commom/config"
"git.toowon.com/jimmy/go-commom/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)
}
注意事项
-
OSS和MinIO SDK实现:
- 当前实现提供了接口和框架,但具体的OSS和MinIO SDK集成需要根据实际使用的SDK实现
- 需要在
oss.go和minio.go中实现具体的SDK调用
-
文件大小限制:
- 建议设置合理的文件大小限制
- 大文件上传可能需要分片上传
-
文件扩展名验证:
- 建议限制允许的文件类型,防止上传恶意文件
- 仅验证扩展名不够安全,建议结合文件内容验证
-
安全性:
- 上传接口应该添加身份验证
- 代理查看接口可以添加访问控制
-
性能优化:
- 对于大文件,考虑使用分片上传
- 代理查看可以添加缓存机制
-
错误处理:
- 所有操作都应该进行错误处理
- 建议记录详细的错误日志
实现OSS和MinIO SDK集成
由于不同的OSS提供商和MinIO有不同的SDK,当前实现提供了框架,需要根据实际情况集成:
OSS SDK集成示例(阿里云OSS)
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集成示例
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