Cloudflare Images 提供了带 CDN 加速的图片存储和转换服务,但通过 PHP 调用时,最容易出问题的地方并不是 API 认证,而是 multipart 表单的构造。如果第一次尝试用 file_get_contents 读取文件内容再直接赋给 CURLOPT_POSTFIELDS,结果收到 400 Bad Request,却不知道错在哪里。实际上 PHP 的 cURL 扩展专门提供了 CURLFile 类来处理文件上传,理解这一点后,整个流程会非常顺畅。

下面从账号参数、本地文件上传、远程 URL 上传以及错误排查几个角度,完整梳理一遍实现过程。
一、准备 Account ID 与 API Token
调用 Cloudflare Images API 之前,需要拿到两个关键参数:Account ID 和 API Token。Account ID 位于 Cloudflare 控制台的域名概览页右侧,是一串 32 位十六进制字符串,也可以从任意一个 Zone 的 Overview 页面找到。API Token 则需要进入 My Profile 下的 API Tokens 页面创建,权限选择 Account - Cloudflare Images - Edit,并限定具体账户资源。不建议使用全局 API Key,因为它权限过大,一旦泄露影响面太广。
创建好 Token 后,可以用一条简单的 curl 命令测试连通性。请求头中需要携带 Authorization: Bearer 加上 Token,URL 中的 account_id 替换成你自己的值。如果返回 success 为 true,说明认证参数已经准备好。
curl -X GET "https://api.cloudflare.com/client/v4/accounts/你的account_id/images/v1" \ -H "Authorization: Bearer 你的api_token"
实际开发中,这两个参数应当放在环境变量或配置文件中,不要硬编码在业务代码里。尤其是 Token,如果进入版本库,可能会被自动化扫描工具标记泄露。
二、上传本地图片文件的正确写法
PHP cURL 上传文件的推荐方式是使用 CURLFile 对象。将 CURLOPT_POSTFIELDS 设置为一个数组,数组的键为 file,值为 new CURLFile(本地路径)。cURL 会自动生成 multipart/form-data 请求体,并附带正确的 Content-Type 和 boundary 边界。注意不要手动设置 Content-Type 请求头,否则会破坏 multipart 边界,服务端无法解析。
下面是一段完整的上传代码。执行前请确认 PHP 版本不低于 5.5,并且文件路径可读。
<?php
$accountId = '你的account_id';
$apiToken = '你的api_token';
$filePath = '/data/images/photo.jpg';
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cloudflare.com/client/v4/accounts/' . $accountId . '/images/v1',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiToken,
],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile($filePath),
],
]);
$response = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
if ($error) {
echo 'cURL Error: ' . $error;
} else {
$data = json_decode($response, true);
if (isset($data['success']) && $data['success']) {
echo '上传成功,主图地址:' . $data['result']['variants'][0];
} else {
echo 'API Error: ' . json_encode($data['errors']);
}
}
?>
为什么不能用 file_get_contents 代替 CURLFile?因为 multipart 表单中文件部分需要包含 filename 和 Content-Type 等元信息。直接把文件内容字符串放进 CURLOPT_POSTFIELDS,PHP 只会把它当作普通的文本字段发送,服务端收到后认为 file 字段不是一个有效的文件,于是返回 400。下面的错误写法可以帮助理解差异。
<?php
// 错误示范:不要这样做
$fileContent = file_get_contents('/data/images/photo.jpg');
$postFields = [
'file' => $fileContent,
];
curl_setopt($curl, CURLOPT_POSTFIELDS, $postFields);
// 服务端会收到 file 字段为二进制字符串,但缺少文件名和文件类型标注
?>
如果文件路径来自用户上传,例如通过表单的 input 文件控件获取的临时文件,需要先用 move_uploaded_file 将文件放到可靠目录,再传给 CURLFile。也可以直接使用临时路径,但临时文件在请求结束后会被清理,务必在脚本结束前完成上传。
三、通过 URL 上传远程图片与附加元数据
Cloudflare Images 还支持直接传入一个公开可访问的图片 URL,由 Cloudflare 服务器下载并入库。此时 CURLOPT_POSTFIELDS 数组中的键改为 url,值为完整的图片地址。PHP 端不需要提前下载文件,这对处理第三方图片源非常方便。
同时可以附加 metadata 字段,它是一个 JSON 字符串,用来给图片打上自定义标签或记录业务 ID,方便后续检索。下面这段代码演示了 URL 上传和 metadata 的使用。
<?php
$accountId = '你的account_id';
$apiToken = '你的api_token';
$remoteImageUrl = 'https://ipipp.com/sample.jpg';
$metadata = [
'product_id' => 10086,
'source' => 'php_curl_demo',
];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => 'https://api.cloudflare.com/client/v4/accounts/' . $accountId . '/images/v1',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiToken,
],
CURLOPT_POSTFIELDS => [
'url' => $remoteImageUrl,
'metadata' => json_encode($metadata),
],
]);
$response = curl_exec($curl);
$error = curl_error($curl);
curl_close($curl);
if ($error) {
echo 'cURL Error: ' . $error;
} else {
$data = json_decode($response, true);
if ($data['success']) {
echo '远程图片已上传,ID:' . $data['result']['id'];
} else {
echo '上传失败:' . json_encode($data['errors'], JSON_UNESCAPED_UNICODE);
}
}
?>
URL 上传对地址的可达性有要求,如果源站设置了防盗链、需要登录或响应时间过长,Cloudflare 可能无法抓取。另外 metadata 必须是合法的 JSON 字符串,建议使用 json_encode 生成,避免手工拼接造成转义错误。元数据写入后可以在 Images 控制台或通过 API 按 metadata 条件查询。
四、解析响应与常见错误排查
上传成功后,响应中最重要的字段是 result.variants。它是一个数组,包含多个不同尺寸或格式的图片访问 URL,通常第一个就是原始比例的主图地址。可以直接存入数据库,作为后续页面展示的资源 URL。result.id 则是图片在 Cloudflare Images 中的唯一标识,删除或查询时都会用到。
如果上传失败,优先检查 HTTP 状态码和响应体中的 errors 数组。下面整理了常见的错误类型与排查方向。
- 400 Bad Request:通常表示 file 或 url 字段缺失,或者 multipart 结构不正确。请确认 CURLOPT_POSTFIELDS 使用了数组,并且键名严格为 file 或 url。
- 401 Unauthorized:Token 无效或已过期,或者 Authorization 头的写法有误。注意 Bearer 后面必须有一个空格。
- 403 Forbidden:Token 权限不足,确认创建时选择了 Cloudflare Images 的 Edit 权限,而不是只读权限。
- 413 Payload Too Large:图片文件超过账户套餐限制,免费版默认单文件最大 10 MB,付费版可以更高。
- 415 Unsupported Media Type:图片格式不在支持列表中。Cloudflare Images 支持常见格式如 PNG、JPEG、WebP、GIF 等,但 SVG 在某些套餐下有额外限制。
还有一种情况是 cURL 本身报错,例如 SSL certificate problem 或 Failed connect。前者需要检查服务器 CA 证书是否过期,后者则是网络出口无法访问 api.cloudflare.com。可以用 curl_error 输出底层错误,再结合服务器防火墙和 DNS 配置逐步定位。
如果希望在上传后立即获得特定尺寸的缩略图,可以通过 Cloudflare Images 的 variants 功能预先定义变体名称,然后在上传响应中按名称取用。合理使用 variants 可以减少前端裁剪的工作量,同时让不同终端获得更合适的图片体积。
PHP cURLCloudflare Images图片上传修改时间:2026-09-18 13:14:20