我认为Flysystem 是磁盘(或其他存储机制)的接口,仅此而已。就像我不会要求本地文件系统计算公共 URI 一样,我也不会要求 Flysystem 这样做。
我创建了与我通过Flysystem 保存的文件相对应的对象。根据我的需要,我可能会将公共 URI 直接保存在数据库记录中,或者我可能会创建一个自定义 getter,以根据运行时环境构建公共 URI。
有了Flysystem,我在写文件的时候就知道了文件的路径。为了跟踪这些文件,我通常会创建一个代表已保存文件的对象:
class SavedFile extends Model
{
protected $fillable = [
'disk',
'path',
'publicURI',
];
}
当我保存文件时,我在数据库中创建了一条记录:
$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,
]);
}
每当我需要公共 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';
}
现在,如果我有一堆文件存储在我的本地文件系统中,并且有一天我将它们移动到 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
编辑:
支持公共和私人文件
只需为对象添加一个标志:
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)