cf前端直接上传

之前是后端上传,现在计划改前端直接传。

使用直接创建者上传还无需中间存储桶,也能省去相关的存储/流出成本

参考文档

Presigned URLs · Cloudflare R2 docs

https://developers.cloudflare.com/r2/api/s3/presigned-urls/ 有php版本sdk

https://developers.cloudflare.com/r2/examples/aws/aws-sdk-php/#generate-presigned-urls

生成秘钥地址

https://developers.cloudflare.com/r2/api/tokens/

示例代码

Route::post('/upload/video/presign', UploadController::class, 'videoPresign');

/**

* 签发视频直传预签名 URL(前端拿 upload_url 直接 PUT 到 R2,文件不过服务端)。

*

* 入参:filename(必填)、content_type、dir、expires

*/

public function videoPresign(Request $request): Response

{

return this-\>run(request, function () use ($request): Response {

payload = this->readParams($request);

$signed = Container::get(UploadService::class)->createVideoUploadUrl([

'filename' => trim((string) ($payload'filename' ?? '')),

'content_type' => trim((string) ($payload'content_type' ?? '')),

'dir' => trim((string) ($payload'dir' ?? '')),

'expires' => $payload'expires' ?? null,

]);

return success($signed, 'success');

});

}

/**

* JSON body 优先,表单提交时回退到 post 参数。

*

* @return array<string, mixed>

*/

private function readParams(Request $request): array

{

if (str_contains(strtolower((string) $request->header('content-type', '')), 'application/json')) {

return this-\>readPayload(request);

}

post = request->post();

return is_array(post) ? post : \[\];

}

/**

* 生成 Cloudflare R2 视频直传预签名 URL。

*

* 后端只负责签发,文件不经过服务端;前端拿到 upload_url 后用 PUT 直传到 R2。

*

* @param array<string, mixed> $options 支持 filename / content_type / dir / expires

* @return array<string, mixed>

*/

public function createVideoUploadUrl(array $options = \[\]): array

{

filename = trim((string) (options'filename' ?? ''));

if ($filename === '') {

throw ValidationException::single('filename', 'required', 'filename is required.');

}

contentType = this->resolveVideoMimeType(options, filename);

r2 = this->resolveR2Config($this->cloudflareConfig());

expires = this->resolveExpires(options, r2);

key = this->buildVideoKey(options, r2, filename, contentType);

objectUrl = sprintf('https://%s/%s', this->r2Host(r2), key);

$presigner = new S3Presigner(

(string) $r2'access_key_id',

(string) $r2'secret_access_key',

trim((string) ($r2'region' ?? 'auto')) ?: 'auto'

);

$requestTime = new DateTimeImmutable('now', new DateTimeZone('UTC'));

signedHeaders = this->shouldSignContentType(r2) ? \['Content-Type' =\> contentType] : \[\];

uploadUrl = presigner->presign('PUT', objectUrl, signedHeaders, expires, requestTime);

return [

'upload_url' => $uploadUrl,

'method' => 'PUT',

// 前端上传时必须原样带上这些请求头,否则签名校验会失败。

'headers' => 'Content-Type' =\> $contentType,

'key' => $key,

'url' => this-\>resolveVideoReadUrl(presigner, r2, objectUrl, key, expires, $requestTime),

'expires_in' => $expires,

'expires_at' => gmdate('Y-m-d\TH:i:s\Z', (int) requestTime-\>getTimestamp() + expires),

];

}

方案 B:绑定自定义域名 video.com(生产推荐)

当前 video.com 指向你的应用服务器,需要修改 DNS,把它指向 R2。

步骤 1:R2 桶内绑定自定义域名

1.Cloudflare 控制台→R2→桶 assets-video→Settings→Custom Domains

2.点击Add custom domain

3.输入域名:video.com

4.选择:Enable public access(开启公开访问)

5.Cloudflare 会给出 DNS 记录提示,需要在域名 DNS 面板添加一条 CNAME 记录

步骤 2:修改 DNS(关键!)

域名 video.com的 DNS 记录:

。删除原有 video.com 指向你应用的 A/AAAA 记录

。新增一条 CNAME 记录:

Name: video

Type: CNAME

Target:<你的桶ID>.r2.cloudflarestorage.com

TTL:自动

注意:这个 CNAME 是 Cloudflare 提供的,不要填 IP。

步骤 3:等待 DNS 生效(几分钟~半小时)

等待 SSL 证书自动颁发完成。

步骤 4:设置环境变量

env ^

句

R2 PUBLIC URL=https://video.com

播放地址格式

plaintext

印 、7

https://video.com/videos/abc123.mp4

这条链路是这样的:

你手动加的 CNAME(upsell → <bucket>.r2.cloudflarestorage.com)✅ 已生效(DNS 查询确认)

但 R2 的 S3 端点只认在「桶 → Custom Domains」里注册过的域名。没注册的话,R2 不认识 Host: video.com 这个请求

结果:Cloudflare 边缘收到请求后按 zone 的常规路由走(CF-RAY ...-LAX 证明到了 Cloudflare),最终落到你的 Ouyify 应用,返回了那个 1972 字节的 404 HTML

结论:手动 CNAME 方式对 R2 是无效的,必须在桶的 Custom Domains 功能里完成绑定------它会自动创建特殊的 DNS 记录(不需要你手动加)、签发证书、并注册路由。

相关推荐
亿元程序员6 小时前
太牛了!这AI做出来的游戏,居然还能变现!
前端
hellice6 小时前
闪传 FlashDrop 实战:我用 300 行 Node 代码做了个「4 位取件码」临时文件中转站
前端·产品
用户0829773260056 小时前
RTX 5500 MIG 踩坑记录:切分之后模型跑不起来,排错整整一下午
前端
Lank_M7 小时前
AI 放大后的图更清楚,但它不能当原图入库
前端
Swot7 小时前
SvelteKit 数据流研究:`+page.js` 和 `+page.server.js` 到底谁在跑,数据又去了哪儿
前端·后端
用户4147679304157 小时前
Windows 触摸屏上 Swiper 轮播滑不动?可能是内部滚动容器抢走了你的手势
前端
张明君644517 小时前
从一个 Referer 引发的 403,到 SSR 服务失稳:Astro 流式渲染与缓存失效分析
前端
律宏阔7 小时前
微信小程序 + Taro + TDesign 去掉 [&&&],让普通 Tailwind 类接管局部样式
前端·微信小程序·taro
费曼学习法7 小时前
把 3 个内部服务封装成 MCP Server 的工程复盘:进程模型、超时与错误契约
前端
Captaincc7 小时前
转译:《软件工程师现在该怎么办》
前端·后端·ai编程