最近在重构博客系统,使用到了 cloudflare 家的 r2 作为图片和媒体资源的存储,使用 sws s3 sdk 构建 presigned url 上传的时候一直报 403 错误,找了半天,按照 cloudflare 家的文档和论坛上的方法实验均不行,

按照文档的代码

javascript
import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"
import { getSignedUrl } from "@aws-sdk/s3-request-presigner"
const S3 = new S3Client({
  endpoint: "https://4893d737c0b9e484dfc37ec392b5fa8a.r2.cloudflarestorage.com",
  credentials: {
    accessKeyId: "7dc27c125a22ad808cd01df8ec309d41",
    secretAccessKey:
      "1aa5c5b0c43defdb88f567487c071d17e234126133444770a706ae09336c57a4",
  },
  region: "auto",
})
const url = await getSignedUrl(
  S3,
  new PutObjectCommand({
    Bucket: bucket,
    Key: object,
  }),
  {
    expiresIn: 60 * 60 * 24 * 7, // 7d
  },
)
console.log(url)

后来发现还是报 cors 错误

"Access to fetch at '' from origin http://localhost:3000 has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."

后来看到需要设置 headers 才可以,也就是必须加入 allowedHeader,通过 put 方法上传文件的时候需要设置 content-type,不然上传后识别不到文件类型,才解决这个跨域的问题,只能说 cloudflare 家的文档也不太行,谷歌一搜好多人遇到这种问题,附上解决方案:

sh
mkdir r2-cors && cd r2-cors
pnpm init
pnpm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
touch cors.js
javascript
import { PutBucketCorsCommand, S3Client } from "@aws-sdk/client-s3";

const s3Client = new S3Client({
  region: "auto",
  endpoint: `https://${CF-ACCOUNT-ID}.r2.cloudflarestorage.com`, //TODO: replace
  credentials: {
    accessKeyId: `${ACCESS-KEY}`, //TODO: replace
    secretAccessKey: `${ACCESS-SECRET}`, //TODO: replace
  },
});

async function main() {
  const response = await s3Client.send(
    new PutBucketCorsCommand({
      Bucket: "your-bucket-name", //TODO: replace
      CORSConfiguration: {
        CORSRules: new Array({
          AllowedHeaders: ["content-type"], //this is important, do not use "*"
          AllowedMethods: ["GET", "PUT", "HEAD"],
          AllowedOrigins: ["*"],
          ExposeHeaders: [],
          MaxAgeSeconds: 3000,
        }),
      },
    })
  );
  console.dir(repsonse)
}

main();

执行 node cors.js

最后的返回结果:

json
{
  '$metadata': {
    httpStatusCode: 200,
    requestId: undefined,
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  }
}

现在就可以愉快的直接通过 presignedurl 上传 文件到 r2 的存储桶了