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 记录(不需要你手动加)、签发证书、并注册路由。

相关推荐
yume_sibai3 小时前
02-Flutter进阶开发
前端·flutter
a1117764 小时前
莱茵生命终端 网页 html
前端·开源
水月清辉4 小时前
CSS 常用语法详解
前端·css
Eric_见嘉5 小时前
打开这个 VSC 设置「组件引用位置」和数量就都清楚了
前端·typescript·visual studio code
李高钢5 小时前
Python Tornado 框架入门:从零搭建你的第一个异步 Web 应用
前端·python·tornado
宁风5 小时前
JavaScript:函数体系-1
前端·javascript
mmsx5 小时前
Android 手簿 ADB 无线调试全攻略:USB 转 WiFi 一键连接
android·前端
guwentian5 小时前
Rolldown vs esbuild vs Webpack:Rust 打包器大战我们该怎么选
前端·webpack·rust·esbuild
特立独行的猫A6 小时前
用纯 Rust 重写 Podcastfy:把网页、PDF 和一段文字,变成一档双人对话播客
前端
oooo_z6 小时前
美股历史K线数据API接口选型指南:iTick覆盖1分钟到月线全周期
服务器·前端·javascript