上传文件到 CloudFlare R2

上周日突然感染流感,发烧一天多,头昏脑涨两三天,只好请假休养。不知道是不是二阳,懒得测。工作进展不多,周末适当加加班,博客适当划划水,这周就记录一下 CloudFlare R2 上传文件吧,实测效果很好,简单省事好用,推荐大家使用。

CloudFlare R2 兼容 AWS S3 API,但是不需要那么复杂的 IAM 体系,而且直接对接 CloudFlare CDN,我觉得更好用。跟国内的众多云存储比起来,它不需要备案,还提供域名和证书,用起来更简单。所以,如果你的网站或者服务需要一个云存储,但是暂时不想负担太高的成本,我建议先试试 CF R2。

我假设读者已经拥有 CF 账号,在 R2 里创建了 bucket,并且完成了需要的配置(好像只需要配置一个域名)。接下来,我们需要在自己的业务代码里实现上传。

首先,我们要安装 AWS SDK。

Terminal
pnpm i @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

接下来,我们需要在项目中建立 S3 配置,其中关键信息都写在 .env 里。

s3.js
import { S3Client } from '@aws-sdk/client-s3'; const S3 = new S3Client({ region: 'auto', endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`, credentials: { accessKeyId: process.env.R2_ACCESS_KEY, secretAccessKey: process.env.R2_SECRET_KEY, }, }); export { S3 };

然后就可以生成预签字的上传路径,相当于提前校验用户身份,这一步需要放在服务器端进行,避免泄漏关键信息。我这里因为业务需求比较简单,没有做复杂的鉴权和检查,如果有需要,就多验证几步。

api/get-upload-url.js
import { PutObjectCommand } from '@aws-sdk/client-s3'; import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; import slugify from 'slugify'; import { H3Event } from 'h3'; import { S3 } from '~/lib/s3'; import { ApiResponse, PreSignedUrl } from '~/types'; export default defineEventHandler(async function (event: H3Event): Promise<ApiResponse<PreSignedUrl>> { // 这一步提交的是预处理信息,没有文件本身,所以还是 json const json = await readBody(event) as { fileName: string | undefined, fileType: string | undefined }; const { fileName, fileType } = json; // 生成存储对象 key,其实就是文件名 const objectKey = `${slugify(Date.now().toString())}-${slugify(fileName)}`; // 生成预签名 url const preSignedUrl = await getSignedUrl(S3, new PutObjectCommand({ Bucket: process.env.PUBLIC_S3_BUCKET_NAME, Key: objectKey, ContentType: fileType, ACL: 'public-read', }), { // 此 URL 5分钟内有效 expiresIn: 60 * 5, // 5 minutes }); return { code: 0, data: { preSignedUrl, objectKey, }, }; });

最后,我们就可以上传文件了。

upload.vue
const response = await $fetch<ApiResponse<PreSignedUrl>>('/api/get-upload-url', { method: 'POST', body: { fileName: file.name, fileType: file.type, }, }); const { preSignedUrl, objectKey } = response.data as PreSignedUrl; const uploadToR2Response = await fetch(preSignedUrl, { method: 'PUT', headers: { 'Content-Type': file.type, }, body: file, }); if (!uploadToR2Response.ok) { console.error('Failed to upload file to R2'); }

整个上传过程比较简单,服务器只需要做一些权限校验,剩下来的过程都可以交给 R2。关键是,这样一来,因为不涉及文件操作,就可以放心交给 Serverless 来做,Vercel 友好。上传后的文件自动进入 CF CDN,也很容易使用。

学会使用 R2 之后没多久,我的 Vercel Blob 存储的申请也通过了。必须得说,这些互联网基础设施对我们全栈开发者来说非常重要,而无障碍的域名对目前在国内还是一种奢望,所以用国外服务就成为一种必然。希望未来会更好吧。

如果您觉得文章内容对您有用,不妨支持我创作更多有价值的分享:


已发布

分类

来自

标签:

评论

欢迎吐槽,共同进步

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理