如何在Laravel 5中获取上传文件的公共URL

Lih*_*hai 9 php filesystems laravel

您可能知道Laravel使用Flysystem PHP包来提供文件系统抽象.我开始在我的项目中使用此功能,只是为了将一些图像上传到我的Amazon s3存储桶,我还将Cloudfront实例链接到此存储桶.我的问题是当我试图在我的html页面中显示这些图像时,我需要一个网址.

我找不到任何"干净"的方式去做,因为flysystem是通用库,我可以做到这样的事情:

Storage::disk('my_awesome_disk')->publicUrl("{$path}/{$image->name}")
Run Code Online (Sandbox Code Playgroud)

对于'公共'文件,它很容易确定文件是否为"公共",因为它包含在他们的api中,所以如果我使用s3 bucket作为我的磁盘驱动程序我应该得到:" https://s3.amazonaws. com/my_awesome_bucket/path/image.png "

或者:

Storage::disk('my_awesome_disk')->signedUrl("{$path}/{$image->name}", $timeout)
Run Code Online (Sandbox Code Playgroud)

对于'私人'文件 - 我应该得到一个暂时的网址,将在一段时间后过期.

这是我只能通过具体实施才能实现的吗?例如,如果我使用亚马逊S3我可以轻松运行:

$signed_url = $s3Client->getObjectUrl($bucket_name,
                        $resource_key,
                        "+{$expires} minutes");
Run Code Online (Sandbox Code Playgroud)

但我不想做丑陋的"切换案例"来确定我使用的驱动程序.我如何通过FileSystem接口从cdn(如cloudfront)获取URL?有什么建议吗?

Ben*_*old 3

我认为它Flysystem是磁盘(或其他存储机制)的接口,仅此而已。正如我不会要求本地文件系统计算公共 URI,我Flysystem也不会要求这样做。

我创建与我通过 保存的文件相对应的对象Flysystem。根据我的需要,我可以将公共 URI 直接保存在数据库记录中,或者我可以创建一个自定义 getter,根据运行时环境构建公共 URI。

有了Flysystem,我在写入文件时就知道了文件的路径。为了跟踪这些文件,我通常会创建一个代表已保存文件的对象:

class SavedFile extends Model
{
    protected $fillable = [
        'disk',
        'path',
        'publicURI',
    ];
}
Run Code Online (Sandbox Code Playgroud)

当我保存文件时,我在数据库中创建它的记录:

$disk     = 'local_example';
$path     = '/path/to/file.txt';
$contents = 'lorem ipsum';
$host     = 'https://example.com/path/to/storage/root/';

if (Store::disk($disk)->put($path, $contents)) {
    SavedFile::create([
        'disk'      => $disk,
        'path'      => $path,
        'publicURI' => $host . $path,
    ]);
}
Run Code Online (Sandbox Code Playgroud)

每当我需要公共 URI 时,我都可以从SavedFile模型中获取它。这对于琐碎的应用程序来说很方便,但如果我需要切换存储提供商,它就会崩溃。

利用继承

另一个巧妙的技巧是定义一个方法,该方法将根据抽象SavedFile模型的子模型中定义的变量来解析公共 URI。这样我就不会在数据库中对 URI 进行硬编码,并且只需几个变量定义就可以为其他存储服务创建新类:

abstract class SavedFile extends Model
{
    protected $table = 'saved_files';

    protected $disk;

    protected $host;

    protected $fillable = [
        'path',
    ];

    public function getPublicURIAttribute()
    {
        return $this->host . $this->attributes['path'];
    }
}

class LocalSavedFile extends SavedFile
{
    $disk = 'local_example';
    $host = 'https://example.com/path/to/storage/root';
}

class AwsSavedFile extends SavedFile
{
    $disk = 'aws_example';
    $host = 'https://s3.amazonaws.com/my_awesome_bucket';
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我在本地文件系统上存储了一堆文件,有一天我将它们移至 Amazon S3,我只需复制这些文件并交换 IoC 绑定定义中的依赖项即可。无需在大型数据库表上执行耗时且有潜在危险的查找和替换,因为 URI 的计算是由模型完成的:

$localFile = LocalSavedFile::create([
    'path' => '/path/to/file.txt'
]);
echo $localFile->publicURI; // https://example.com/path/to/storage/root/path/to/file.txt

$awsFile = AwsSavedFile::find($localFile->id);
echo $awsFile->publicURI; // https://s3.amazonaws.com/my_awesome_bucket/path/to/file.txt
Run Code Online (Sandbox Code Playgroud)

编辑:

支持公共和私人文件

只需向对象添加一个标志:

abstract class SavedFile extends Model
{
    protected $table = 'saved_files';

    protected $disk;

    protected $host;

    protected $fillable = [
        'path',
        'public',
    ];

    public function getPublicURIAttribute()
    {
        if ($this->attributes['public'] === false) {
            // you could throw an exception or return a default image if you prefer
            return false;
        }

        return $this->host . $this->attributes['path'];
    }
}

class LocalSavedFile extends SavedFile
{
    $disk = 'local_example';
    $host = 'https://example.com/path/to/storage/root';
}

$localFile = LocalSavedFile::create([
    'path'   => '/path/to/file.txt',
    'public' => false,
]);
var_dump($localFile->publicURI); // bool(false)
Run Code Online (Sandbox Code Playgroud)