添加元数据,headers (Expires, CacheControl) 使用 Laravel 5.0 Storage facade 上传到 Amazon S3 的文件
Add Metadata, headers (Expires, CacheControl) to a file uploaded to Amazon S3 using the Laravel 5.0 Storage facade
我正在尝试了解如何将元数据或 headers(Expires、CacheControl 等)添加到使用 Laravel 5.0 Storage facade 上传的文件中。我已经使用这里的页面作为参考。
http://laravel.com/docs/5.0/filesystem
以下代码可以正常工作:
Storage::disk('s3')->put('/test.txt', 'test');
在挖掘之后我还发现有一个 'visibility' 参数将 ACL 设置为 'public-read' 所以下面的也可以正常工作。
Storage::disk('s3')->put('/test.txt', 'test', 'public');
但我希望能够为文件的 header 设置一些其他值。我尝试了以下方法:
Storage::disk('s3')->put('/index4.txt', 'test', 'public', array('Expires'=>'Expires, Fri, 30 Oct 1998 14:19:41 GMT'));
哪个不行,我也试过了:
Storage::disk('s3')->put('/index4.txt', 'test', array('ACL'=>'public-read'));
但这会产生错误,其中 'visibility' 参数无法从字符串转换为数组。我检查了 AwsS3Adapter 的来源,似乎有选项代码,但我似乎看不到如何正确传递它们。我认为需要以下条件:
protected static $metaOptions = [
'CacheControl',
'Expires',
'StorageClass',
'ServerSideEncryption',
'Metadata',
'ACL',
'ContentType',
'ContentDisposition',
'ContentLanguage',
'ContentEncoding',
];
任何有关如何完成此操作的帮助将不胜感激。
嘿我解决了这个问题,你需要创建一个自定义的 S3 文件系统
首先,创建一个新文件 CustomS3Filesystem.php 并保存到 app/providers,此自定义 S3 文件系统使用 S3 适配器,但您可以添加元数据和 headers。
<?php namespace App\Providers;
use Storage;
use League\Flysystem\Filesystem;
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v2\AwsS3Adapter as S3Adapter;
use Illuminate\Support\ServiceProvider;
class CustomS3Filesystem extends ServiceProvider {
public function boot()
{
Storage::extend('s3_custom', function($app, $config)
{
$s3Config = array_only($config, ['key', 'region', 'secret', 'signature', 'base_url']);
$flysystemConfig = ['mimetype' => 'text/xml'];
$metadata['cache_control']='max-age=0, no-cache, no-store, must-revalidate';
return new Filesystem(new S3Adapter(S3Client::factory($s3Config), $config['bucket'], null, ['mimetype' => 'text/xml', 'Metadata' => $metadata]), $flysystemConfig);
});
}
public function register()
{
//
}
}
将供应商添加到供应商列表中 config/app。php
'App\Providers\CustomS3Filesystem',
在 config/filesystems
中创建新的文件系统名称
's3-new' => [
'driver' => 's3_custom',
'key' => 'XXX',
'secret' => 'XXX',
'bucket' => 'XXX',
],
使用新创建的自定义 s3 适配器
Storage::disk('s3-new')->put(filename, file_get_contents($file), public);
我使用 laravel 文档来自定义 s3 适配器
http://laravel.com/docs/5.0/filesystem#custom-filesystems
希望对您有所帮助。
首先,您需要调用 getDriver
以便发送一组选项。然后您需要将选项作为数组发送。
所以对于你的例子:
Storage::disk('s3')->getDriver()->put('/index4.txt', 'test', [ 'visibility' => 'public', 'Expires' => 'Expires, Fri, 30 Oct 1998 14:19:41 GMT']);
请注意,如果您要设置 Cache-Control
,则必须将其作为 CacheControl
传递。对于具有非字母数字字符的其他键,这可能也是正确的。
我正在使用 Laravel 4.2,但我认为我的解决方案也可能对 Laravel 5.0 有所帮助(不能肯定地说,因为我还没有尝试升级)。您需要为正在使用的 Flysystem 驱动程序更新配置中的元选项。在我的例子中,我创建了一个名为 s3static 的连接来访问我存储不会更改的图像的存储桶。
我的配置文件:
's3static' => [
'driver' => 'awss3',
'key' => 'my-key',
'secret' => 'my-secret',
'bucket' => 'my-bucket',
// 'region' => 'your-region',
// 'base_url' => 'your-url',
'options' => array(
'CacheControl' => 'max_age=2592000'
),
// 'prefix' => 'your-prefix',
// 'visibility' => 'public',
// 'eventable' => true,
// 'cache' => 'foo'
],
现在,当我使用此连接将任何文件放到 S3 上时,它们都有 Cache-Control 元数据集。
为了扩展@sergiodebcn 的回答,这里是适用于 S3 v3 和最新 Laravel 的同一个 CustomS3Filesystem class。注意我已经删除了 XML mimetype 并设置了 5 天的缓存时间:
namespace App\Providers;
use Illuminate\Support\Arr;
use Storage;
use League\Flysystem\Filesystem;
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v3\AwsS3Adapter as S3Adapter;
use Illuminate\Support\ServiceProvider;
class CustomS3Filesystem extends ServiceProvider
{
/**
* Format the given S3 configuration with the default options.
*
* @param array $config
* @return array
*/
protected function formatS3Config(array $config)
{
$config += ['version' => 'latest'];
if ($config['key'] && $config['secret']) {
$config['credentials'] = Arr::only($config, ['key', 'secret']);
}
return $config;
}
/**
* Bootstrap a custom filesystem
*
* @return void
*/
public function boot()
{
Storage::extend('s3_custom', function($app, $config)
{
$s3Config = $this->formatS3Config($config);
return new Filesystem(
new S3Adapter(
new S3Client($s3Config),
$config['bucket'],
null,
[
'CacheControl' => 'max-age=432000'
]
)
);
});
}
public function register()
{
//
}
}
如果您想使用 headers 的全局默认值,这适用于 Laravel 5.4。像这样更改 config/filesystems.php
文件:
s3' => [
'driver' => 's3',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
'options' => ['CacheControl' => 'max-age=315360000, no-transform, public',
'ContentEncoding' => 'gzip']
],
@Paras 的回答很好。但是有一件事会让新手感到困惑:
'options' => [
'Expires' => gmdate('D, d M Y H:i:s GMT', strtotime('+1 month')),
>>> WRONG visibility' => 'public', WRONG <<<
]
如果要为 HEADERS 定义全局选项,选项数组是正确的方法。但如果您还想定义可见性,则不能混淆。可见性必须在选项数组之外定义。
'visibility' => 'public',
'options' => ['Expires' => gmdate('D, d M Y H:i:s GMT', strtotime('+1 month'))]
在尝试了上述答案但无法添加客户用户元数据后,发现在深入了解 SDK 代码后,它比我想象的要容易一些(假设 $path
是一条通往图像文件)。我似乎也不需要调用 getDriver()
方法,不太确定这是否与当前版本的 AWS SDK 有任何区别。
Storage::put(
'image.jpg',
file_get_contents($path),
[
'visibility' => 'public',
'Metadata' => [
'thumb' => '320-180',
],
]
);
现在如果您在 S3 中查看新上传的文件,您将看到自定义元数据:
希望这对某人有所帮助。
这是一个示例,说明如何从 Laravel 5.8 起将文件上传到 S3,并使用到期和缓存控制 headers,例如:
Storage::put($directory . '/' . $imageName,
$image, [
'visibility' => 'public',
'Expires' => gmdate('D, d M Y H:i:s \G\M\T', time() + (60 * 60 * 24 * 7)),
'CacheControl' => 'max-age=315360000, no-transform, public',
]);
另外,如果您正在测试并且它似乎永远不会工作,请不要忘记取消选中 Chrome 中的 'Disable cache' 复选框,这让我的浏览器无法正常工作了一个小时缓存东西,即使我终于在 S3 中获得了 headers。
对于 Laravel 9 用户,这变得更加容易。您不再需要调用 ->getDriver()
。您可以直接将选项传递给 put
命令。
Storage::disk('s3')->put('/index.txt', 'file content', [
// S3 Object ACL
'visibility' => 'public', // or 'private',
// HTTP Headers
'CacheControl' => 'public,max-age=315360000',
'ContentDisposition' => 'attachment; filename="index.txt"',
'Expires' => 'Thu, 12 Feb 2032 08:24:43 GMT',
// Metadata or other S3 options
'MetadataDirective' => 'REPLACE'
'Metadata' => [
'Custom-Key' => 'test',
],
])
如果您需要其他 headers 或选项,请查看 flysystem 源代码以获得所有可用的 headers 和选项。
https://github.com/thephpleague/flysystem-aws-s3-v3/blob/master/src/AwsS3Adapter.php#L38
public const AVAILABLE_OPTIONS = [
'ACL',
'CacheControl',
'ContentDisposition',
'ContentEncoding',
'ContentLength',
'ContentType',
'Expires',
'GrantFullControl',
'GrantRead',
'GrantReadACP',
'GrantWriteACP',
'Metadata',
'MetadataDirective',
'RequestPayer',
'SSECustomerAlgorithm',
'SSECustomerKey',
'SSECustomerKeyMD5',
'SSEKMSKeyId',
'ServerSideEncryption',
'StorageClass',
'Tagging',
'WebsiteRedirectLocation',
];
在这里使用 Laravel 8:
我没有在其他地方看到这个,但是 Christoph Kluge
列出的元数据选项键 => 值
似乎只接受字符串值,如果传递一个整数、布尔值等,则会无声地失败...因此,如果您传递的是变量,则需要转换为字符串值:
$fileID = $fileData['FileId'];
$fileExt = $fileData['FileExtension'];
$fileUnique = $fileData['UniqueFileId'];
$isImage = $fileData['IsImage'];
$isDefault = $fileData['IsDefaultImage'];
$filePath = $fileUnique . "." . $fileExt;
$file = $mp->fileID($fileID)->get();
if (Storage::disk('s3')->missing('img/' . $filePath)) {
Storage::disk('s3')->put(
'img/' . $filePath,
$file,
[
// Metadata or other S3 options
'MetadataDirective' => 'REPLACE',
'Metadata' => [
'is-image' => strval($isImage),
'is-default' => strval($isDefault),
'unique-file-id' => strval($fileUnique),
'file-extension' => strval($fileExt),
]
]
);
echo nl2br('uploading file: ' . $filePath . "\n");
} else {
echo nl2br('file already exists:' . $filePath . "\n");
}
我正在尝试了解如何将元数据或 headers(Expires、CacheControl 等)添加到使用 Laravel 5.0 Storage facade 上传的文件中。我已经使用这里的页面作为参考。
http://laravel.com/docs/5.0/filesystem
以下代码可以正常工作:
Storage::disk('s3')->put('/test.txt', 'test');
在挖掘之后我还发现有一个 'visibility' 参数将 ACL 设置为 'public-read' 所以下面的也可以正常工作。
Storage::disk('s3')->put('/test.txt', 'test', 'public');
但我希望能够为文件的 header 设置一些其他值。我尝试了以下方法:
Storage::disk('s3')->put('/index4.txt', 'test', 'public', array('Expires'=>'Expires, Fri, 30 Oct 1998 14:19:41 GMT'));
哪个不行,我也试过了:
Storage::disk('s3')->put('/index4.txt', 'test', array('ACL'=>'public-read'));
但这会产生错误,其中 'visibility' 参数无法从字符串转换为数组。我检查了 AwsS3Adapter 的来源,似乎有选项代码,但我似乎看不到如何正确传递它们。我认为需要以下条件:
protected static $metaOptions = [
'CacheControl',
'Expires',
'StorageClass',
'ServerSideEncryption',
'Metadata',
'ACL',
'ContentType',
'ContentDisposition',
'ContentLanguage',
'ContentEncoding',
];
任何有关如何完成此操作的帮助将不胜感激。
嘿我解决了这个问题,你需要创建一个自定义的 S3 文件系统
首先,创建一个新文件 CustomS3Filesystem.php 并保存到 app/providers,此自定义 S3 文件系统使用 S3 适配器,但您可以添加元数据和 headers。
<?php namespace App\Providers;
use Storage;
use League\Flysystem\Filesystem;
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v2\AwsS3Adapter as S3Adapter;
use Illuminate\Support\ServiceProvider;
class CustomS3Filesystem extends ServiceProvider {
public function boot()
{
Storage::extend('s3_custom', function($app, $config)
{
$s3Config = array_only($config, ['key', 'region', 'secret', 'signature', 'base_url']);
$flysystemConfig = ['mimetype' => 'text/xml'];
$metadata['cache_control']='max-age=0, no-cache, no-store, must-revalidate';
return new Filesystem(new S3Adapter(S3Client::factory($s3Config), $config['bucket'], null, ['mimetype' => 'text/xml', 'Metadata' => $metadata]), $flysystemConfig);
});
}
public function register()
{
//
}
}
将供应商添加到供应商列表中 config/app。php
'App\Providers\CustomS3Filesystem',
在 config/filesystems
中创建新的文件系统名称's3-new' => [
'driver' => 's3_custom',
'key' => 'XXX',
'secret' => 'XXX',
'bucket' => 'XXX',
],
使用新创建的自定义 s3 适配器
Storage::disk('s3-new')->put(filename, file_get_contents($file), public);
我使用 laravel 文档来自定义 s3 适配器 http://laravel.com/docs/5.0/filesystem#custom-filesystems
希望对您有所帮助。
首先,您需要调用 getDriver
以便发送一组选项。然后您需要将选项作为数组发送。
所以对于你的例子:
Storage::disk('s3')->getDriver()->put('/index4.txt', 'test', [ 'visibility' => 'public', 'Expires' => 'Expires, Fri, 30 Oct 1998 14:19:41 GMT']);
请注意,如果您要设置 Cache-Control
,则必须将其作为 CacheControl
传递。对于具有非字母数字字符的其他键,这可能也是正确的。
我正在使用 Laravel 4.2,但我认为我的解决方案也可能对 Laravel 5.0 有所帮助(不能肯定地说,因为我还没有尝试升级)。您需要为正在使用的 Flysystem 驱动程序更新配置中的元选项。在我的例子中,我创建了一个名为 s3static 的连接来访问我存储不会更改的图像的存储桶。
我的配置文件:
's3static' => [
'driver' => 'awss3',
'key' => 'my-key',
'secret' => 'my-secret',
'bucket' => 'my-bucket',
// 'region' => 'your-region',
// 'base_url' => 'your-url',
'options' => array(
'CacheControl' => 'max_age=2592000'
),
// 'prefix' => 'your-prefix',
// 'visibility' => 'public',
// 'eventable' => true,
// 'cache' => 'foo'
],
现在,当我使用此连接将任何文件放到 S3 上时,它们都有 Cache-Control 元数据集。
为了扩展@sergiodebcn 的回答,这里是适用于 S3 v3 和最新 Laravel 的同一个 CustomS3Filesystem class。注意我已经删除了 XML mimetype 并设置了 5 天的缓存时间:
namespace App\Providers;
use Illuminate\Support\Arr;
use Storage;
use League\Flysystem\Filesystem;
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v3\AwsS3Adapter as S3Adapter;
use Illuminate\Support\ServiceProvider;
class CustomS3Filesystem extends ServiceProvider
{
/**
* Format the given S3 configuration with the default options.
*
* @param array $config
* @return array
*/
protected function formatS3Config(array $config)
{
$config += ['version' => 'latest'];
if ($config['key'] && $config['secret']) {
$config['credentials'] = Arr::only($config, ['key', 'secret']);
}
return $config;
}
/**
* Bootstrap a custom filesystem
*
* @return void
*/
public function boot()
{
Storage::extend('s3_custom', function($app, $config)
{
$s3Config = $this->formatS3Config($config);
return new Filesystem(
new S3Adapter(
new S3Client($s3Config),
$config['bucket'],
null,
[
'CacheControl' => 'max-age=432000'
]
)
);
});
}
public function register()
{
//
}
}
如果您想使用 headers 的全局默认值,这适用于 Laravel 5.4。像这样更改 config/filesystems.php
文件:
s3' => [
'driver' => 's3',
'key' => env('AWS_KEY'),
'secret' => env('AWS_SECRET'),
'region' => env('AWS_REGION'),
'bucket' => env('AWS_BUCKET'),
'options' => ['CacheControl' => 'max-age=315360000, no-transform, public',
'ContentEncoding' => 'gzip']
],
@Paras 的回答很好。但是有一件事会让新手感到困惑:
'options' => [
'Expires' => gmdate('D, d M Y H:i:s GMT', strtotime('+1 month')),
>>> WRONG visibility' => 'public', WRONG <<<
]
如果要为 HEADERS 定义全局选项,选项数组是正确的方法。但如果您还想定义可见性,则不能混淆。可见性必须在选项数组之外定义。
'visibility' => 'public',
'options' => ['Expires' => gmdate('D, d M Y H:i:s GMT', strtotime('+1 month'))]
在尝试了上述答案但无法添加客户用户元数据后,发现在深入了解 SDK 代码后,它比我想象的要容易一些(假设 $path
是一条通往图像文件)。我似乎也不需要调用 getDriver()
方法,不太确定这是否与当前版本的 AWS SDK 有任何区别。
Storage::put(
'image.jpg',
file_get_contents($path),
[
'visibility' => 'public',
'Metadata' => [
'thumb' => '320-180',
],
]
);
现在如果您在 S3 中查看新上传的文件,您将看到自定义元数据:
希望这对某人有所帮助。
这是一个示例,说明如何从 Laravel 5.8 起将文件上传到 S3,并使用到期和缓存控制 headers,例如:
Storage::put($directory . '/' . $imageName,
$image, [
'visibility' => 'public',
'Expires' => gmdate('D, d M Y H:i:s \G\M\T', time() + (60 * 60 * 24 * 7)),
'CacheControl' => 'max-age=315360000, no-transform, public',
]);
另外,如果您正在测试并且它似乎永远不会工作,请不要忘记取消选中 Chrome 中的 'Disable cache' 复选框,这让我的浏览器无法正常工作了一个小时缓存东西,即使我终于在 S3 中获得了 headers。
对于 Laravel 9 用户,这变得更加容易。您不再需要调用 ->getDriver()
。您可以直接将选项传递给 put
命令。
Storage::disk('s3')->put('/index.txt', 'file content', [
// S3 Object ACL
'visibility' => 'public', // or 'private',
// HTTP Headers
'CacheControl' => 'public,max-age=315360000',
'ContentDisposition' => 'attachment; filename="index.txt"',
'Expires' => 'Thu, 12 Feb 2032 08:24:43 GMT',
// Metadata or other S3 options
'MetadataDirective' => 'REPLACE'
'Metadata' => [
'Custom-Key' => 'test',
],
])
如果您需要其他 headers 或选项,请查看 flysystem 源代码以获得所有可用的 headers 和选项。
https://github.com/thephpleague/flysystem-aws-s3-v3/blob/master/src/AwsS3Adapter.php#L38
public const AVAILABLE_OPTIONS = [
'ACL',
'CacheControl',
'ContentDisposition',
'ContentEncoding',
'ContentLength',
'ContentType',
'Expires',
'GrantFullControl',
'GrantRead',
'GrantReadACP',
'GrantWriteACP',
'Metadata',
'MetadataDirective',
'RequestPayer',
'SSECustomerAlgorithm',
'SSECustomerKey',
'SSECustomerKeyMD5',
'SSEKMSKeyId',
'ServerSideEncryption',
'StorageClass',
'Tagging',
'WebsiteRedirectLocation',
];
在这里使用 Laravel 8:
我没有在其他地方看到这个,但是 Christoph Kluge
列出的元数据选项键 => 值似乎只接受字符串值,如果传递一个整数、布尔值等,则会无声地失败...因此,如果您传递的是变量,则需要转换为字符串值:
$fileID = $fileData['FileId'];
$fileExt = $fileData['FileExtension'];
$fileUnique = $fileData['UniqueFileId'];
$isImage = $fileData['IsImage'];
$isDefault = $fileData['IsDefaultImage'];
$filePath = $fileUnique . "." . $fileExt;
$file = $mp->fileID($fileID)->get();
if (Storage::disk('s3')->missing('img/' . $filePath)) {
Storage::disk('s3')->put(
'img/' . $filePath,
$file,
[
// Metadata or other S3 options
'MetadataDirective' => 'REPLACE',
'Metadata' => [
'is-image' => strval($isImage),
'is-default' => strval($isDefault),
'unique-file-id' => strval($fileUnique),
'file-extension' => strval($fileExt),
]
]
);
echo nl2br('uploading file: ' . $filePath . "\n");
} else {
echo nl2br('file already exists:' . $filePath . "\n");
}