使用 Go 上传文件到 Cloudflare R2 的完整指南

91 min read

Cloudflare R2 为对象存储提供了一个相对 AWS S3 更具成本效益的替代方案,最大的优势是没有出站流量费用。在本教程中,我们将探讨如何使用 Go 语言通过 AWS SDK for Go v2 来上传文件到 Cloudflare R2,充分利用 R2 兼容 S3 的 API。

前置要求

在开始之前,请确保你具备:

  • 系统中已安装 Go 语言环境
  • 已启用 R2 功能的 Cloudflare 账户
  • R2 的访问凭证(账户 ID、访问密钥 ID 和密钥)
  • 基本的 Go 编程知识

项目设置

首先,创建一个新的 Go 项目并安装所需的 AWS SDK 包:

mkdir r2-upload
cd r2-upload
go mod init r2-upload
go get github.com/aws/aws-sdk-go-v2
go get github.com/aws/aws-sdk-go-v2/config
go get github.com/aws/aws-sdk-go-v2/credentials
go get github.com/aws/aws-sdk-go-v2/service/s3

具体实现

以下是一个完整的示例,展示如何将文件上传到 Cloudflare R2:

package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/aws/aws-sdk-go-v2/aws"
    "github.com/aws/aws-sdk-go-v2/config"
    "github.com/aws/aws-sdk-go-v2/credentials"
    "github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
    // R2 凭证
    accountID := "your_account_id"
    accessKeyID := "your_access_key_id"
    accessKeySecret := "your_access_key_secret"

    // 自定义 R2 端点解析器
    r2Resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
        return aws.Endpoint{
            URL: fmt.Sprintf("https://%s.r2.cloudflarestorage.com", accountID),
        }, nil
    })

    // 配置 AWS SDK
    cfg, err := config.LoadDefaultConfig(context.TODO(),
        config.WithEndpointResolverWithOptions(r2Resolver),
        config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, accessKeySecret, "")),
        config.WithRegion("auto"),
    )
    if err != nil {
        log.Fatal(err)
    }

    // 创建 S3 客户端
    client := s3.NewFromConfig(cfg)

    // 打开要上传的文件
    file, err := os.Open("example.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    // 上传文件
    _, err = client.PutObject(context.TODO(), &s3.PutObjectInput{
        Bucket: aws.String("your-bucket-name"),
        Key:    aws.String("example.txt"),
        Body:   file,
    })
    if err != nil {
        log.Fatal(err)
    }

    log.Println("文件上传成功")
}

代码解析

让我们来分析代码的关键部分:

1. 配置设置

r2Resolver := aws.EndpointResolverWithOptionsFunc(func(service, region string, options ...interface{}) (aws.Endpoint, error) {
    return aws.Endpoint{
        URL: fmt.Sprintf("https://%s.r2.cloudflarestorage.com", accountID),
    }, nil
})

这段代码创建了一个自定义端点解析器,指向你的 R2 存储桶的端点。URL 格式是 Cloudflare R2 特有的。

2. AWS SDK 配置

cfg, err := config.LoadDefaultConfig(context.TODO(),
    config.WithEndpointResolverWithOptions(r2Resolver),
    config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, accessKeySecret, "")),
    config.WithRegion("auto"),
)

这里配置了 AWS SDK,包括你的 R2 凭证和自定义端点解析器。注意我们使用 "auto" 作为区域,因为 R2 不使用传统的 AWS 区域。

3. 文件上传

_, err = client.PutObject(context.TODO(), &s3.PutObjectInput{
    Bucket: aws.String("your-bucket-name"),
    Key:    aws.String("example.txt"),
    Body:   file,
})

这段代码将文件上传到你的 R2 存储桶。Key 参数决定了文件在存储桶中的名称。

最佳实践和提示

  1. 错误处理:始终在每个步骤检查错误,特别是在文件操作和上传过程中。

  2. 文件管理:使用 defer file.Close() 确保文件在使用后正确关闭。

  3. 安全性:永远不要在代码中硬编码凭证。请使用环境变量或配置文件:

accountID := os.Getenv("R2_ACCOUNT_ID")
accessKeyID := os.Getenv("R2_ACCESS_KEY_ID")
accessKeySecret := os.Getenv("R2_ACCESS_KEY_SECRET")
  1. 内容类型:你可以指定上传文件的内容类型:
_, err = client.PutObject(context.TODO(), &s3.PutObjectInput{
    Bucket:      aws.String("your-bucket-name"),
    Key:         aws.String("example.txt"),
    Body:        file,
    ContentType: aws.String("text/plain"),
})

常见问题和解决方案

  1. 连接问题
  • 仔细检查你的账户 ID 和端点 URL
  • 验证 R2 存储桶是否存在且配置正确
  • 确保防火墙没有阻止连接
  1. 身份验证错误
  • 验证访问密钥 ID 和密钥是否正确
  • 检查 R2 令牌是否具有必要的权限
  • 确保你的凭证没有过期

总结

使用 Go 与 Cloudflare R2 结合为应用程序提供了一个强大且经济的对象存储解决方案。AWS SDK 的 S3 兼容性使其实现变得简单直接,而 R2 的定价模型(特别是没有出站流量费用)使其成为许多用例的理想选择。

记住在生产代码中始终遵循安全最佳实践并正确处理错误。对于更高级的用法,你可能想要探索其他功能,如大文件的分片上传或实现重试逻辑以提高可靠性。

相关资源