支持cf的r2存储

This commit is contained in:
amos wong
2025-12-14 20:46:45 +08:00
parent 0edaa855db
commit a3378d646e
3 changed files with 58 additions and 14 deletions

View File

@@ -4,12 +4,12 @@ DB_PATH=./data/memory.db
JWT_SECRET=your-secret-key-change-in-production
BASE_URL=http://192.168.0.100:8080
# 本地存储 (优先使用,留空则使用R2)
LOCAL_UPLOAD_PATH=./uploads
# 本地存储 (留空则使用R2)
LOCAL_UPLOAD_PATH=
# Cloudflare R2 (可选LOCAL_UPLOAD_PATH为空时使用)
# Cloudflare R2
R2_ACCOUNT_ID=your-account-id
R2_ACCESS_KEY_ID=your-access-key-id
R2_ACCESS_KEY_SECRET=your-access-key-secret
R2_BUCKET_NAME=memory
R2_PUBLIC_URL=https://your-bucket.r2.dev
R2_PUBLIC_URL=

View File

@@ -99,11 +99,11 @@ func (h *UploadHandler) Upload(c *gin.Context) {
}
defer src.Close()
// 优先使用本地存储
if h.cfg.LocalUploadPath != "" {
url, err := h.saveLocal(src, filename)
// 优先使用 R2 存储
if h.s3Client != nil {
url, err := h.uploadToR2(src, filename, contentType)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save file"})
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to upload file: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
@@ -113,11 +113,11 @@ func (h *UploadHandler) Upload(c *gin.Context) {
return
}
// 使用 R2 存储
if h.s3Client != nil {
url, err := h.uploadToR2(src, filename, contentType)
// 使用本地存储
if h.cfg.LocalUploadPath != "" {
url, err := h.saveLocal(src, filename)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to upload file"})
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save file"})
return
}
c.JSON(http.StatusOK, gin.H{
@@ -166,6 +166,47 @@ func (h *UploadHandler) uploadToR2(src io.Reader, filename, contentType string)
return "", err
}
url := fmt.Sprintf("%s/%s", strings.TrimSuffix(h.cfg.R2PublicURL, "/"), filename)
return url, nil
// 如果配置了公开 URL直接返回
if h.cfg.R2PublicURL != "" {
return fmt.Sprintf("%s/%s", strings.TrimSuffix(h.cfg.R2PublicURL, "/"), filename), nil
}
// 否则通过服务端代理访问
return fmt.Sprintf("%s/files/%s", strings.TrimSuffix(h.cfg.BaseURL, "/"), filename), nil
}
// GetFile 代理访问 R2 文件
func (h *UploadHandler) GetFile(c *gin.Context) {
// 获取文件路径 (格式: 2024/01/uuid.jpg)
filepath := c.Param("filepath")
if filepath == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "filepath required"})
return
}
// 从 R2 获取文件
if h.s3Client != nil {
result, err := h.s3Client.GetObject(context.TODO(), &s3.GetObjectInput{
Bucket: aws.String(h.cfg.R2BucketName),
Key: aws.String(filepath),
})
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
return
}
defer result.Body.Close()
// 设置响应头
if result.ContentType != nil {
c.Header("Content-Type", *result.ContentType)
}
c.Header("Cache-Control", "public, max-age=31536000")
// 流式传输
c.Status(http.StatusOK)
io.Copy(c.Writer, result.Body)
return
}
c.JSON(http.StatusNotFound, gin.H{"error": "file not found"})
}

View File

@@ -40,6 +40,9 @@ func Setup(db *sql.DB, cfg *config.Config) *gin.Engine {
searchHandler := handler.NewSearchHandler(db, cfg)
uploadHandler := handler.NewUploadHandler(cfg)
// R2 文件代理 (公开访问)
r.GET("/files/*filepath", uploadHandler.GetFile)
// 公开接口
r.POST("/api/auth/register", authHandler.Register)
r.POST("/api/auth/login", authHandler.Login)