使用 PHP 与 AWS SDK 开发 Ceph RGW(S3 兼容)应用:从连接创建到签名 URL 的完整指南
存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载本文以 Ceph 对象网关RADOS Gateway简称 RGW的 S3 兼容接口为核心基于官方文档 doc/radosgw/s3/php.rst 的 PHP 示例系统讲解如何通过 AWS PHP SDK 完成连接创建、桶与对象的增删改查、ACL 权限控制以及签名 URL 生成等完整操作。读完本文你将能够在自己的 PHP 项目中用最少的代码把 Ceph RGW 当作标准 S3 服务使用并理解每个 API 调用背后的服务端行为与分页、权限等关键细节。环境准备安装 AWS PHP SDK在开始编写代码之前需要先安装 AWS 官方 PHP SDKaws/aws-sdk-php。Ceph RGW 兼容 S3 协议因此该 SDK 是官方推荐且兼容性最好的 PHP 客户端之一。如果你还没有安装 ComposerPHP 的依赖管理工具请先完成 Composer 的安装然后在项目根目录执行$ composer install aws/aws-sdk-php安装完成后Composer 会生成vendor/目录与自动加载文件vendor/autoload.php后续所有示例代码都通过require __DIR__./vendor/autoload.php;引入 SDK。创建与 RGW 服务的连接与任何 S3 兼容服务交互的第一步是实例化Aws\S3\S3Client。针对 Ceph RGW初始化参数有四个关键点regionCeph RGW 不依赖 AWS 区域概念但 SDK 强制要求该字段因此置为空字符串version指定 S3 API 版本Ceph 文档中的示例使用2006-03-01endpointRGW 服务地址例如http://objects.dreamhost.com示例为外部兼容服务接入自建 Ceph RGW 时替换为你的网关地址如http://ceph-rgw.example.comuse_path_style_endpoint置为true让 SDK 使用endpoint/bucket的路径风格寻址而不是bucket.endpoint的虚拟主机风格。?php use Aws\S3\S3Client; define(AWS_KEY, place access key here); define(AWS_SECRET_KEY, place secret key here); $ENDPOINT http://objects.dreamhost.com; // require the Amazon SDK from your composer vendor dir require __DIR__./vendor/autoload.php; // Instantiate the S3 class and point it at the desired host $client new S3Client([ region , version 2006-03-01, endpoint $ENDPOINT, credentials [ key AWS_KEY, secret AWS_SECRET_KEY ], // Set the S3 class to use objects.dreamhost.com/bucket // instead of bucket.objects.dreamhost.com use_path_style_endpoint true ]);提示AWS_KEY与AWS_SECRET_KEY对应 RGW 为用户分配的 Access Key 与 Secret Key。在生产环境中应通过环境变量、配置文件或密钥管理服务注入避免硬编码在源码中。路径风格与虚拟主机风格use_path_style_endpoint涉及 S3 两种桶寻址模式详见 doc/radosgw/s3/commons.rst路径风格path-style桶名作为请求路径的顶级目录例如GET /mybucket HTTP/1.1Host: cname.domain.com虚拟主机风格virtual-hosted-style桶名作为完全限定域名的一部分例如GET / HTTP/1.1Host: mybucket.cname.domain.com这也是多数 S3 客户端默认采用的方式。AWS 已宣布逐步废弃路径风格访问但 Ceph RGW 同时支持两种模式。若你希望 RGW 正确处理带桶名的主机头需要在 Monitor 配置库中设置rgw_dns_name或在 zonegroup 配置的hostnames列表中声明网关域名ceph config set client.rgw.ceph authx client for rgw rgw_dns_name cname.domain.com从源码看RGW 在 src/rgw/rgw_rest.cc 中读取rgw_dns_name并按逗号与空格拆分出多个合法主机名get_str_list(rgw_dns_names_str, , , rgw_dns_names)随后在第 2161 行将其作为请求的 domain 字段参与桶名解析。因此该参数直接决定了虚拟主机风格访问能否命中正确的桶在 PHP SDK 中显式设置use_path_style_endpoint true则可以绕开 DNS 解析环节直接以路径风格访问。补充当 RGW 部署在反向代理之后时应使用代理的 DNS 名启用 SSL 时证书需要包含匹配桶子域名的通配符S3 静态网站视图s3websiteAPI使用独立的rgw_dns_s3website_name配置与rgw_dns_name分开设置。桶Bucket操作列出用户拥有的桶listBuckets()返回一个AWS\Result实例可以用数组下标方式访问。下面代码遍历所有桶并打印桶名与创建时间?php $listResponse $client-listBuckets(); $buckets $listResponse[Buckets]; foreach ($buckets as $bucket) { echo $bucket[Name] . \t . $bucket[CreationDate] . \n; }输出类似mahbuckat1 2011-04-21T18:05:39.000Z mahbuckat2 2011-04-21T18:05:48.000Z mahbuckat3 2011-04-21T18:07:18.000Z创建桶创建一个名为my-new-bucket的桶同样返回AWS\Result对象?php $client-createBucket([Bucket my-new-bucket]);创建时可通过ACL参数直接指定桶级 canned ACL也可稍后用putBucketAcl修改。若同名桶已存在RGW 会返回409 BucketAlreadyExists见 doc/radosgw/s3/commons.rst 的公共响应码表。列出桶中的对象listObjects返回桶内对象列表每个对象包含Key对象名、Size字节大小与LastModified最后修改时间?php $objectsListResponse $client-listObjects([Bucket $bucketname]); $objects $objectsListResponse[Contents] ?? []; foreach ($objects as $object) { echo $object[Key] . \t . $object[Size] . \t . $object[LastModified] . \n; }输出类似myphoto1.jpg 251262 2011-08-08T21:35:48.000Z myphoto2.jpg 262518 2011-08-08T21:38:01.000Z注意重要如果桶内对象超过 1000 个一次listObjects无法返回全部结果。此时必须检查响应中的isTruncated字段若为true则把本次结果最后一个对象的 Key 作为Marker参数再次请求循环直到isTruncated不再为真。?php $marker null; do { $params [Bucket $bucketname]; if ($marker ! null) { $params[Marker] $marker; } $response $client-listObjects($params); foreach (($response[Contents] ?? []) as $object) { echo $object[Key] . \t . $object[Size] . \t . $object[LastModified] . \n; $marker $object[Key]; } } while ($response[IsTruncated]);这一分页行为在 RGW 服务端有硬性约束从源码看RGW 默认单页最多返回 1000 个条目src/rgw/driver/rados/rgw_bucket.cc 中constexpr uint64_t max_keys 1000;并且 RGW 会对客户端传入的max-keys做安全钳制——RGWListBucket::parse_max_keys()将max-keys绑定在 0 到配置上限之间src/rgw/rgw_op.cc并在 src/rgw/rgw_op.cc 中以do...while (results.is_truncated)形式消费所有分页。因此客户端侧务必实现上述游标循环才能完整枚举大桶。删除桶删除名为my-old-bucket的桶?php $client-deleteBucket([Bucket my-old-bucket]);注意重要桶必须为空才能删除如果桶内仍有对象RGW 会返回409 BucketNotEmpty。正确流程是先枚举并删除桶内所有对象可配合deleteObjects批量删除再删除桶。对象Object操作上传对象putObject将字符串内容写入指定桶的对象。下面创建一个内容为Hello World!的hello.txt?php $client-putObject([ Bucket my-bucket-name, Key hello.txt, Body Hello World! ]);Body可以是字符串、资源流或Psr\Http\Message\StreamInterface实现因此也适用于上传本地文件或大文件流大文件建议改用MultipartUploader进行分片上传。修改对象的 ACL对象默认继承桶的权限策略默认私有。putObjectAcl允许对单个对象设置 canned ACL。下面把hello.txt设为公开可读把secret_plans.txt设为私有?php $client-putObjectAcl([ Bucket my-bucket-name, Key hello.txt, ACL public-read ]); $client-putObjectAcl([ Bucket my-bucket-name, Key secret_plans.txt, ACL private ]);这些 canned ACL 在 RGW 服务端的语义由 src/rgw/rgw_acl_s3.cc 中的create_canned()函数实现规则如下canned ACL服务端实现对应源码分支private或空仅所有者拥有FULL_CONTROL不附加任何授权L420-L422public-read所有者FULL_CONTROL 所有用户组ACL_GROUP_ALL_USERS获得READL424-L427public-read-write所有者FULL_CONTROL 所有用户组获得READ与WRITEL428-L433authenticated-read所有者FULL_CONTROL 已认证用户组获得READL434-L437bucket-owner-read/bucket-owner-full-control当桶所有者与对象所有者不同时授予桶所有者对应权限L438-L449从源码可以确认public-read的本质是向匿名用户组授予读权限这正是后续未签名 URL 能够直接访问hello.txt的原因而private对象只有所有者可访问必须借助签名 URL。删除对象删除名为goodbye.txt的对象?php $client-deleteObject([Bucket my-bucket-name, Key goodbye.txt]);下载对象到本地文件getObject返回对象内容Body是一个流对象可通过getContents()取回完整内容并写入本地文件。下面把poetry.pdf下载到/home/larry/documents/?php $object $client-getObject([Bucket my-bucket-name, Key poetry.pdf]); file_put_contents(/home/larry/documents/poetry.pdf, $object[Body]-getContents());对于大对象更推荐把Body流直接拷贝到本地文件句柄例如用GuzzleHttp\Psr7\Stream与copy_to_stream避免一次性把整个对象载入内存。生成对象下载 URL签名与未签名Ceph RGW 支持通过预签名 URLpresigned URL向他人提供限时下载能力也支持为公开对象直接生成未签名 URL。未签名 URL使用getObjectUrl生成仅对公开对象有效如上面设置了public-read的hello.txt签名 URL先通过getCommand(GetObject, ...)构造命令再用createPresignedRequest指定有效期。签名 URL 在有效期内即使对象是私有的也能下载到期后自动失效。?php $hello_url $client-getObjectUrl(my-bucket-name, hello.txt); echo $hello_url.\n; $secret_plans_cmd $client-getCommand(GetObject, [Bucket my-bucket-name, Key secret_plans.txt]); $request $client-createPresignedRequest($secret_plans_cmd, 1 hour); echo $request-getUri().\n;输出类似http://objects.dreamhost.com/my-bucket-name/hello.txt http://objects.dreamhost.com/my-bucket-name/secret_plans.txt?X-Amz-Content-Sha256UNSIGNED-PAYLOADX-Amz-AlgorithmAWS4-HMAC-SHA256X-Amz-CredentialsandboxAccessKey%2F20190116%2F%2Fs3%2Faws4_requestX-Amz-Date20190116T125520ZX-Amz-SignedHeadershostX-Amz-Expires3600X-Amz-Signature61921f07c73d7695e47a2192cf55ae030f34c44c512b2160bb5a936b2b48d923从生成的 URL 可以看到签名机制的几个关键参数X-Amz-AlgorithmAWS4-HMAC-SHA256表明使用 AWS 签名 V4 算法RGW 在 src/rgw/rgw_auth.h 中实现了对 AWS Auth V4 的解析与过滤X-Amz-Credential包含 Access Key 与签发时间X-Amz-Expires3600表示有效期 3600 秒即 1 小时X-Amz-Signature是对请求的 HMAC-SHA256 签名。浏览器直接访问该 URL 即可下载无需携带任何凭证——这正是预签名 URL 适合分发给临时用户的典型用法。常见错误码速查使用 PHP SDK 与 RGW 交互时SDK 会将服务端返回的 HTTP 状态码转换为对应的S3Exception。结合 doc/radosgw/s3/commons.rst 的公共响应码表以下几个状态码在 PHP 开发中最常遇到HTTP 状态码错误码触发场景400InvalidBucketName/InvalidObjectName桶名或对象名不符合 S3 命名规范403AccessDenied凭证缺失、签名错误或权限不足如访问私有对象404NoSuchBucket/NoSuchKey桶或对象不存在409BucketAlreadyExists创建已存在的桶409BucketNotEmpty删除非空桶500InternalErrorRGW 服务端内部错误捕获异常的推荐写法?php use Aws\Exception\S3Exception; try { $client-getObject([Bucket my-bucket-name, Key missing.txt]); } catch (S3Exception $e) { echo HTTP 状态码: . $e-getStatusCode() . \n; echo 错误码: . $e-getAwsErrorCode() . \n; echo 错误信息: . $e-getAwsErrorMessage() . \n; }完整示例一键串联全流程将上述各步骤整合为一个可独立运行的脚本便于快速验证 RGW 环境?php use Aws\S3\S3Client; use Aws\Exception\S3Exception; require __DIR__./vendor/autoload.php; $client new S3Client([ region , version 2006-03-01, endpoint http://ceph-rgw.example.com, // 替换为你的 RGW 地址 credentials [ key YOUR_ACCESS_KEY, secret YOUR_SECRET_KEY, ], use_path_style_endpoint true, ]); $bucket demo-bucket- . time(); // 1. 创建桶 $client-createBucket([Bucket $bucket]); // 2. 上传对象 $client-putObject([ Bucket $bucket, Key hello.txt, Body Hello World!, ]); // 3. 设为公开读 $client-putObjectAcl([ Bucket $bucket, Key hello.txt, ACL public-read, ]); // 4. 列出对象 $objects $client-listObjects([Bucket $bucket])[Contents] ?? []; foreach ($objects as $object) { echo $object[Key] . \t . $object[Size] . \t . $object[LastModified] . \n; } // 5. 生成签名 URL1 小时有效 $cmd $client-getCommand(GetObject, [Bucket $bucket, Key hello.txt]); $presigned $client-createPresignedRequest($cmd, 1 hour); echo $presigned-getUri() . \n; // 6. 删除对象与桶先清空桶 $client-deleteObject([Bucket $bucket, Key hello.txt]); $client-deleteBucket([Bucket $bucket]);总结通过 AWS PHP SDK 访问 Ceph RGW 的完整路径可以概括为composer安装 SDK → 以空 region、自定义 endpoint 与路径风格初始化S3Client→ 依次完成桶与对象的增删改查、ACL 设置与预签名 URL 生成。理解use_path_style_endpoint与rgw_dns_name的对应关系、listObjects的 1000 条分页上限、canned ACL 在 src/rgw/rgw_acl_s3.cc 中的实现语义以及 AWS4-HMAC-SHA256 签名 URL 的生成机制能帮助你在实际项目中少走弯路把 Ceph RGW 稳定、安全地接入 PHP 应用。更多语言示例可参考 doc/radosgw/s3 目录下的 Python、Java、Ruby 等对应文档。赞分享存储分布式文件系统对象存储后端高可用【免费下载链接】cephCeph is a distributed object, block, and file storage platform项目地址https://gitcode.com/gh_mirrors/ce/ceph点击查看免费下载相关推荐使用 C AWS SDK 对接 Ceph RGW S3 网关从连接建立、桶与对象操作到预签名 URL 的完整实践使用 C AWS SDK 对接 Ceph RGW S3 网关从连接建立、桶与对象操作到预签名 URL 的完整实践 本文以 Ceph RADOS Gateway存储分布式文件系统对象存储后端高可用使用 Ruby AWS SDK 访问 Ceph RGW S3 接口桶与对象操作完整指南使用 Ruby AWS SDK 访问 Ceph RGW S3 接口桶与对象操作完整指南 导读 Ceph Object GatewayRGW对外提供与 Am存储分布式文件系统对象存储后端高可用Ceph RGW S3 兼容性测试套件从 s3-tests 到 STS / IAM / Bucket Logging 的完整实战指南Ceph RGW S3 兼容性测试套件从 s3 tests 到 STS / IAM / Bucket Logging 的完整实战指南 本指南基于 Ceph 仓存储分布式文件系统对象存储后端高可用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考