使用 AWS SDK for PHP 版本 3 的 Amazon S3 Stream Wrapper - AWS SDK for PHP

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

使用 AWS SDK for PHP 版本 3 的 Amazon S3 Stream Wrapper

Amazon S3 Stream Wrapper 支持您使用内置 PHP 函数从 Amazon S3 存储和检索数据,这些函数包括 file_get_contentsfopencopyrenameunlinkmkdirrmdir

您需要注册 Amazon S3 Stream Wrapper 才可使用。

$client = new Aws\S3\S3Client([/** options **/]); // Register the stream wrapper from an S3Client object $client->registerStreamWrapper();

这样您就可以使用 s3:// 协议来访问 Amazon S3 中存储的存储桶和对象。Amazon S3 Stream Wrapper 接受包含存储桶名称的字符串,后跟正斜杠和可选的对象键或前缀:s3://<bucket>[/<key-or-prefix>]

注意

Stream Wrapper 可处理您至少拥有读取权限的对象和存储桶。这就意味着,您的用户应有权针对任何需要交互的存储桶执行 ListBucket,并针对任何需要交互的对象执行 GetObject。对于不具有此级别权限的使用情形,建议您直接使用 Amazon S3 客户端操作。

下载数据

您可以使用 file_get_contents 获取对象内容。但使用此功能要小心,它会将对象的全部内容载入内存。

// Download the body of the "key" object in the "bucket" bucket $data = file_get_contents('s3://bucket/key');

如果处理较大文件或需要从 Amazon S3 流式传输数据,请使用 fopen()

// Open a stream in read-only mode if ($stream = fopen('s3://bucket/key', 'r')) { // While the stream is still open while (!feof($stream)) { // Read 1,024 bytes from the stream echo fread($stream, 1024); } // Be sure to close the stream resource when you're done with it fclose($stream); }
注意

只有调用 fflush 才会返回文件写入错误。如果调用了未刷新的 fclose,不会返回这些错误。如果 fclose 关闭了流,它的返回值将为 true;无论其内部 fflush 是否会响应任何错误。如果调用 file_put_contents 也不会返回这些错误,这是由于 PHP 的实施方式导致的。

打开可搜寻的流

以“r”模式打开的流仅允许读取流中的数据,默认情况下不可搜寻。这样数据才能够真正以流式处理的方式从 Amazon S3 下载,之前读取的字节不需要缓冲到内存中。如果需要对流进行搜寻,可以将 seekable 传递到函数的流上下文选项

$context = stream_context_create([ 's3' => ['seekable' => true] ]); if ($stream = fopen('s3://bucket/key', 'r', false, $context)) { // Read bytes from the stream fread($stream, 1024); // Seek back to the beginning of the stream fseek($stream, 0); // Read the same bytes that were previously read fread($stream, 1024); fclose($stream); }

“打开可搜寻的流”支持您搜寻之前读取的字节。您不可跳转到尚未从远程服务器读取的字节。要允许重新调用之前读取的数据,需使用流装饰器将数据缓冲到 PHP 临时流中。如果缓存数据量超过 2 MB,临时流中的数据会从内存传输到磁盘中。在使用 seekable 流上下文设置从 Amazon S3 下载大文件时,请留意这一点。

上传数据

您可以使用 file_put_contents() 来将数据上传到 Amazon S3。

file_put_contents('s3://bucket/key', 'Hello!');

您可以结合使用 fopen() 和“w”、“x”或“a”流访问模式来流式传输数据,从而上传较大的文件。Amazon S3 Stream Wrapper 支持同时读取和写入流(例如“r+”、“w+”等)。这是因为 HTTP 协议不允许同时读取和写入。

$stream = fopen('s3://bucket/key', 'w'); fwrite($stream, 'Hello!'); fclose($stream);
注意

Amazon S3 需要在发送请求负载之前指定内容长度标题。因此,在 PutObject 操作中上传的数据会使用 PHP 临时流进行内部缓冲,直到流刷新或关闭。

注意

只有调用 fflush 才会返回文件写入错误。如果调用了未刷新的 fclose,不会返回这些错误。如果 fclose 关闭了流,它的返回值将为 true;无论其内部 fflush 是否会响应任何错误。如果调用 file_put_contents 也不会返回这些错误,这是由于 PHP 的实施方式导致的。

fopen 模式

PHP 的 fopen() 函数需要您指定 $mode 选项。mode 选项指定数据是否可在流中进行读写,以及打开流时文件是否必须存在。

对于以 Amazon S3 对象为目标的流,Amazon S3 Stream Wrapper 支持以下模式。

r

只读流,对象必须已存在。

w

只可写入的流。如果对象已存在,则将覆盖该对象。

a

只可写入的流。如果对象已存在,则会将它下载到临时流中,并且对流的任何写入操作将追加到之前上传的数据后面。

x

只可写入的流。如果对象已存在,将引发错误。

其他对象函数

Stream Wrapper 允许多种不同的内置 PHP 函数与 Amazon S3 这样的自定义系统配合使用。Amazon S3 Stream Wrapper 允许您针对存储在 Amazon S3 中的对象执行以下函数。

unlink()

从存储桶中删除一个对象。

// Delete an object from a bucket unlink('s3://bucket/key');

您可以传递 DeleteObject 操作的任何可用选项,修改对象的删除方式(例如指定具体的对象版本)。

// Delete a specific version of an object from a bucket unlink('s3://bucket/key', stream_context_create([ 's3' => ['VersionId' => '123'] ]);

filesize()

获取对象的大小。

// Get the Content-Length of an object $size = filesize('s3://bucket/key', );

is_file()

检查 URL 是否为文件。

if (is_file('s3://bucket/key')) { echo 'It is a file!'; }

file_exists()

检查某个对象是否存在。

if (file_exists('s3://bucket/key')) { echo 'It exists!'; }

filetype()

检查 URL 是否对应于文件或存储桶 (dir)。

file()

将对象内容加载到一些行中。您可以传递 GetObject 操作的任何可用选项,修改文件的下载方式。

filemtime()

获取对象的最新修改日期。

rename()

复制对象并删除原始版本,从而对对象重命名。您可以将 CopyObjectDeleteObject 操作的可用选项传递到流上下文参数,修改复制和删除对象的方式。

注意

虽然 copy 一般与 Amazon S3 Stream Wrapper 配合使用,由于 PHP 中 copy 函数的内部原因,某些错误可能不会正确报告。我们建议您使用 AwsS3ObjectCopier 的实例作为替代。

使用存储桶和文件夹

使用 mkdir() 来使用存储桶

您可以创建和浏览 Amazon S3 存储桶,这与 PHP 允许您在文件系统上创建和遍历目录的方式类似。

以下是创建存储桶的示例。

mkdir('s3://my-bucket');
注意

2023 年 4 月,Amazon S3 自动启用了 S3 屏蔽公共访问权限,并对所有新创建的存储桶禁用了访问控制列表。此更改还会影响到 StreamWrappermkdir 函数与权限和 ACL 的配合方式。有关更多信息,可参阅 AWS 新发展一文

您可以将流上下文选项传递到 mkdir() 方法,使用 CreateBucket 操作可用的参数修改存储桶的创建方式。

// Create a bucket in the EU (Ireland) Region mkdir('s3://my-bucket', 0500, true, stream_context_create([ 's3' => ['LocationConstraint' => 'eu-west-1'] ]));

您可以使用 rmdir() 函数删除存储桶。

// Delete a bucket rmdir('s3://my-bucket);
注意

只有空存储桶才可删除。

使用 mkdir() 来使用文件夹

创建存储桶后,您可以使用 mkdir() 来创建对象,这些对象可用作文件夹,就像在文件系统中一样。

以下代码段将名为“my-folder”的文件夹对象添加到名为“my-bucket”的现有存储桶中。使用正斜杠 (/) 字符将文件夹对象名称与存储桶名称和任何其他文件夹名称分开。

mkdir('s3://my-bucket/my-folder')

关于 2023 年 4 月之后权限变更的上一个注释也会在您创建文件夹对象时发挥作用。这篇博客文章包含有关如何在需要时调整权限的信息。

使用 rmdir() 函数删除空文件夹对象,如下面的代码段所示。

rmdir('s3://my-bucket/my-folder')

列出存储桶的内容。

可将 opendir()readdir()rewinddir()closedir() PHP 函数与 Amazon S3 Stream Wrapper 配合使用,遍历存储桶的内容。您可以将 ListObjects 操作的可用参数作为 opendir() 函数的自定义流上下文选项进行传递,以修改呈现对象的方式。

$dir = "s3://bucket/"; if (is_dir($dir) && ($dh = opendir($dir))) { while (($file = readdir($dh)) !== false) { echo "filename: {$file} : filetype: " . filetype($dir . $file) . "\n"; } closedir($dh); }

您可以使用 PHP 的 RecursiveDirectoryIterator 以递归方式列出存储桶中的每个对象和前缀。

$dir = 's3://bucket'; $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)); foreach ($iterator as $file) { echo $file->getType() . ': ' . $file . "\n"; }

还可以使用 Aws\recursive_dir_iterator($path, $context = null) 函数以递归方式列出存储桶内容,这样可产生较少的 HTTP 请求。

<?php require 'vendor/autoload.php'; $iter = Aws\recursive_dir_iterator('s3://bucket/key'); foreach ($iter as $filename) { echo $filename . "\n"; }

流上下文选项

您可以通过传递自定义流上下文选项,自定义 Stream Wrapper 使用的客户端,或用于缓存之前加载的存储桶和键信息的缓存。

Stream Wrapper 针对每个操作支持以下流上下文选项。

client

Aws\AwsClientInterface 对象,用于执行命令。

cache

Aws\CacheInterface 的实例可用于缓存之前获得的文件统计数据。默认情况下,Stream Wrapper 使用内存中 LRU 缓存。