gsmeira / laravel-attachments
Requires
- php: ^8.1
- illuminate/database: ^9.52 || ^10.0
- illuminate/filesystem: ^9.52 || ^10.0
- illuminate/http: ^9.52 || ^10.0
- illuminate/support: ^9.52 || ^10.0
- illuminate/validation: ^9.52 || ^10.0
Requires (Dev)
- orchestra/testbench: ^8.10
- pestphp/pest: ^2.18
README
- ✅ 无缝集成
- ✅ 灵活配置
- ✅ 适用于无服务器环境
- ✅ 一个JSON列可以存储记录中的所有文件
- ✅ 将预定义数据追加到文件基础路径
- ✅ 内置处理和验证预签名url响应
- ✅ 路径混淆
安装
1 — 此包需要PHP 8.1和至少Laravel 9.52。要获取最新版本,只需使用Composer即可。
composer require gsmeira/laravel-attachments
2 — 安装完成后,如果您不使用自动包发现,那么您需要将GSMeira\LaravelAttachments\AttachmentsServiceProvider
服务提供者在您的config/app.php
文件中注册。
3 — 发布配置文件。
php artisan vendor:publish --tag=laravel-attachments
在您的应用config
文件夹中将创建一个名为attachments.php
的文件。
用法
1 — 在您的.env
文件上设置文件系统磁盘。
FILESYSTEM_DISK=<disk>
2 — 在您的模型表的模型中添加一个名为attachments
的json
列。如果您使用的是PostgreSQL,也可以使用jsonb
列。
$table->json('attachments')->nullable();
3 — 将HasAttachments
特质添加到您的模型。
class MyModel extends Model { use HasAttachments; }
4 — 创建或更新时传递有效的附件值。
MyModel::create([ // ... 'attachments' => [ 'file' => $request->file, ], ]);
$myModel->update([ // ... 'attachments' => [ 'file' => $request->file, ], ]);
文件将存储在attachments
列中,如下所示
{ "file": "Y5EsfRvITY4SEyP3IBwUMurkkpRo69DfEMEIRlp9.jpg" }
重要: 附件有两个接受值。第一个是文件直接作为UploadedFile
值发送到服务器的情况。第二个是使用预签名url,并期望一个包含有效path
键的array
的情况。我们将在关于预签名url的部分中更详细地讨论第二种类型。
它是如何工作的?
在使用此包时,您需要了解的主要规则是,每当设置attachments
属性时,都会在新的值和之前已存储的值(如果存在)之间执行合并操作。因此,即使您存储了多个文件,您也可以对它们执行单独的操作。以下所有示例即使在存储了多个文件的情况下也可以正常工作。
如何更新/创建附件?
选项1 — 要更新附件,只需为要更新的键设置新值。旧文件将从存储磁盘永久删除。
$myModel->update([ 'attachments' => [ 'file' => $request->file, ], ]);
选项2 — 第二种方法是使用updateAttachments
方法。
$myModel->updateAttachments([ 'file' => $request->file, ]);
如何删除附件?
选项1 — 将要删除的附件设置为null
(或任何无效值)。
$myModel->update([ 'attachments' => [ 'file' => null, ], ]);
将删除存储磁盘上的file
附件,并将键从附件列中删除。
选项2 — 第二种方法是使用deleteAttachment
方法。
$myModel->deleteAttachment('file');
如果您要删除多个文件,可以向此方法提供一个数组。
$myModel->deleteAttachment(['file', 'cover_image']);
如何删除所有附件?
选项1 — 要删除所有文件,可以将附件设置为null
。
$myModel->update([ 'attachments' => null, ]);
选项2 — 您还可以使用deleteAttachments
方法。
$myModel->delateAttachments();
删除模型记录
每次删除具有 HasAttachments
特性的模型记录时,附件列中的所有文件将从存储磁盘上删除。如果你的模型中存在 SoftDeletes
特性,则只有在 forceDelete
时才会删除文件。
全局配置
位于 config/attachments.php
文件中。
文件
file.base_folder
所有附件都将存储的文件夹。 默认: ''
file.appends
将数据库中存储的路径字符串转换为包含额外信息的数组。如果为空,则不会附加到路径。 默认: [ AttachmentsAppend::Path, AttachmentsAppend::Url, AttachmentsAppend::Exists ]
路径
path_obfuscation.enabled
确定路径混淆是否激活。 默认: false
path_obfuscation.levels
定义路径的深度。 默认: 3
签名存储
signed_storage.enabled
如果为 true
,则将注册生成预签名URL的路由。 默认: false
signed_storage.temp_folder
使用预签名URL时存储文件的临时文件夹。 默认: tmp
signed_storage.expire_after
预签名URL的过期时间(分钟)。 默认: 5
signed_storage.route
预签名URL的路由配置。
模型配置
如果你想进行更细粒度的配置,可以直接在模型文件中自定义一些选项。
public function attachmentsBaseFolder(): string { return 'users'; }
public function isAttachmentsPathObfuscationEnabled(): bool { return true; }
public function attachmentsPathObfuscationLevels(): int { return 2; }
public function attachmentsDisk(): string { return 'private'; }
预签名URL
当使用预签名URL时,你的文件将存储在s3等服务中,文件不再通过服务器发送。相反,应直接从前端上传到云服务,并将此文件的引用发送到服务器。此包期望一个包含应与存储服务中文件位置匹配的 path
的数组。
如何使用这种方法?
1 — 在Laravel应用程序中配置s3 并记得将文件系统磁盘设置为 s3
。
FILESYSTEM_DISK=s3
2 — 为你的存储桶设置CORS策略,以便向其发送文件。
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "PUT" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ]
重要:请注意,在生产环境中,你可能需要比上面的示例更严格的限制。
3 — 在 attachments.php
配置文件中启用和配置预签名URL端点。
4 — 创建一个端点以接收预签名URL响应中的文件。示例
protected function update(User $user, Request $request) { $request->validate([ 'file' => ['required', new PreSignedAttachmentRule], ]); $user->update([ // ... 'attachments' => [ 'file' => $request->file, ], ]); // ... }
始终使用 PreSignedAttachmentRule
检查签名文件是否有效。此规则将检查值是否是包含 path
键的数组,并检查路径是否存在于 s3
上。
5 — 在你的前端,你应该有类似以下的内容。
<input id="file" type="file" />
import axios from 'axios' axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest' const host = 'https:///' async function generatePreSignedUrlAndStore(file) { const response = await axios.post(`${host}/attachments/signed-storage-url`) const headers = response.data.headers if ('Host' in headers) delete headers.Host await axios.put(response.data.url, file, { headers }) response.data.extension = file.name.split('.').pop() return response.data } document.querySelector('#file').addEventListener('change', (evt) => { const file = evt.target.files[0] generatePreSignedUrlAndStore(file).then((response) => { axios.post(`${host}/profile-image`, { file: { path: response.path, name: file.name, content_type: file.type, }, }) }) })
测试
在1.0版本发布之前,此包将被完全测试。
许可证
Laravel Attachments 在 MIT 许可证(MIT) 下授权。