nadlambino / uploadable
自动处理模型文件上传。
Requires
- php: ^8.2
- illuminate/contracts: ^10.0||^11.0
- spatie/laravel-package-tools: ^1.16
Requires (Dev)
- larastan/larastan: ^2.9
- laravel/pint: ^1.14
- nunomaduro/collision: ^8.1.1||^7.10.0
- orchestra/testbench: ^9.0.0||^8.22.0
- pestphp/pest: ^2.34
- pestphp/pest-plugin-arch: ^2.7
- pestphp/pest-plugin-laravel: ^2.3
- phpstan/extension-installer: ^1.3
- phpstan/phpstan-deprecation-rules: ^1.1
- phpstan/phpstan-phpunit: ^1.3
README
目录
安装
您可以通过composer安装此包
composer require nadlambino/uploadable
使用以下命令发布并运行迁移
php artisan vendor:publish --tag="uploadable-migrations"
php artisan migrate
重要
根据需要,您可以添加更多字段到上传表中,但现有字段应保持不变。
可选地,您可以使用以下命令发布Upload模型
php artisan vendor:publish --tag="uploadable-model"
您可以使用以下命令发布配置文件
php artisan vendor:publish --tag="uploadable-config"
这是已发布配置文件的内容
uploadable.php
return [ /* |-------------------------------------------------------------------------- | Validation |-------------------------------------------------------------------------- | | Enable or disable the package's validation rules. Set to false if validation | has been performed separately, such as in a form request. | */ 'validate' => true, /* |-------------------------------------------------------------------------- | Uploads Model |-------------------------------------------------------------------------- | | Specify the model to use for uploads. | */ 'uploads_model' => \NadLambino\Uploadable\Models\Upload::class, /* |-------------------------------------------------------------------------- | Delete Model on Upload Failure |-------------------------------------------------------------------------- | | Automatically delete the newly created model if the upload process fails. | Applicable only to models that are being created. | */ 'delete_model_on_upload_fail' => true, /* |-------------------------------------------------------------------------- | Rollback Model on Upload Failure |-------------------------------------------------------------------------- | | Revert changes made to an existing model if the upload fails, restoring | the model's original attributes. Applies only to updated models. | */ 'rollback_model_on_upload_fail' => true, /* |-------------------------------------------------------------------------- | Force Delete Uploads |-------------------------------------------------------------------------- | | Determines whether uploaded files are permanently deleted. By default, | files are soft deleted, allowing for recovery. | */ 'force_delete_uploads' => false, /* |-------------------------------------------------------------------------- | Replace Previous Uploads |-------------------------------------------------------------------------- | | Determines whether uploaded files should be replaced with new ones. If | false, new files will be uploaded. If true, previous files will be | deleted once the new ones are uploaded. | */ 'replace_previous_uploads' => false, /* |-------------------------------------------------------------------------- | Upload Queue |-------------------------------------------------------------------------- | | Specify the queue name for uploading files. If set to null, uploads are | processed immediately. Otherwise, files are queued and processed. | */ 'upload_on_queue' => null, /* |-------------------------------------------------------------------------- | Delete Model on Queued Upload Failure |-------------------------------------------------------------------------- | | Delete the newly created model if a queued upload fails. Only affects models | that are being created. | */ 'delete_model_on_queue_upload_fail' => false, /* |-------------------------------------------------------------------------- | Rollback Model on Queued Upload Failure |-------------------------------------------------------------------------- | | Revert changes to a model if a queued upload fails, using the model's original | attributes before the upload started. Affects only updated models. | */ 'rollback_model_on_queue_upload_fail' => false, /* |-------------------------------------------------------------------------- | Temporary Storage Disk |-------------------------------------------------------------------------- | | Define the disk for temporary file storage during queued uploads. This | is where files are stored before being processed. | */ 'temporary_disk' => 'local', /* |-------------------------------------------------------------------------- | Temporary URL |-------------------------------------------------------------------------- | | Temporary URL for files that are uploaded locally is not supported by the | local disk. This setting allows you to specify the path and middleware to | access the files temporarily which uses a signed URL under the hood. | `expiration` can be a string or an instance of `DateTimeInterface`. | */ 'temporary_url' => [ 'path' => '/temporary', 'middleware' => ['signed'], 'expiration' => '1 hour', ], /* |-------------------------------------------------------------------------- | Allowed Mimes by Extension |-------------------------------------------------------------------------- | | Specify the mime types by extension that is allowed for uploads. Supports | categorization for images, videos, and documents with specific file | extensions. | */ 'mimes' => [ 'image' => ['jpeg', 'jpg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico'], 'video' => ['mp4', 'webm', 'avi', 'mov', 'wmv', 'flv', '3gp', 'mkv', 'mpg', 'mpeg'], 'document' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'csv', 'txt'], ], ];
使用
只需将 NadLambino\Uploadable\Concerns\Uploadable
特性应用到需要文件上传的模型中。
namespace App\Models; use Illuminate\Database\Eloquent\Model; use NadLambino\Uploadable\Concerns\Uploadable; class Post extends Model { use Uploadable; }
现在,每次您创建或更新帖子时,它将自动上传请求中包含的文件,并将详细信息保存到 uploads
表中。
自定义
1. 规则和消息
请求中的文件应具有以下请求名称
您可以通过在模型中定义受保护的 uploadRules
方法来添加更多字段或覆盖默认字段。
protected function uploadRules(): array { return [ // Override the rules for `document` field 'document' => ['required', 'file', 'mime:application/pdf'], // Add a new field with it's own set of rules 'avatar' => ['required', 'image', 'mime:png'] ]; }
要添加或覆盖规则消息,您可以在模型中定义受保护的 uploadRuleMessages
方法。
public function uploadRuleMessages(): array { return [ 'document.required' => 'The file is required.', 'document.mime' => 'The file must be a PDF file.', 'avatar.required' => 'The avatar is required.', 'avatar.mime' => 'The avatar must be a PNG file.' ]; }
2. 文件名和上传路径
您可以通过在模型中定义公共方法 getUploadFilename
和 getUploadPath
来自定义文件名和路径。
public function getUploadFilename(UploadedFile $file): string { return str_replace('.', '', microtime(true)).'-'.$file->hashName(); } public function getUploadPath(UploadedFile $file): string { return $this->getTable().DIRECTORY_SEPARATOR.$this->{$this->getKeyName()}; }
重要
确保文件名完全唯一,以避免覆盖现有文件。
3. 存储选项
当您在云存储上上传文件时,您可能经常想提供诸如可见性、缓存控制和其他元数据之类的选项。为此,您可以在模型中定义 getUploadStorageOptions
。
public function getUploadStorageOptions(): array { return [ 'visibility' => 'public', 'CacheControl' => 'max-age=315360000, no-transform, public' ]; }
4. 上传磁盘
当您上传文件时,有时您只是将其上传到本地磁盘。但是,对于更大的文件,您可能想使用 s3
。这可以通过静态方法 uploadDisk
实现。
// Let us say that on your config/filesystems.php, your default disk is set to `local`. // This method will create a user and will upload the file from the request, e.g., user avatar. public function store(Request $request) { User::create(...); } // While this method store the message and will upload the file from the request to `s3`. public function store(Request $request) { Message::uploadDisk('s3'); Message::create(...); }
5. 上传分组
当您为模型上传文件时,有时您可以为不同的目的进行多个文件上传。例如,帖子可以有一个缩略图、横幅和相册的上传。这可以通过 uploadToCollection
方法实现。
public function store(Request $request) { Post::uploadToCollection('banner'); Post::create(...); }
此外,要检索特定集合的文件上传,您可以使用查询作用域 fromCollection
。
$post = Post::query() ->with('image', fn ($query) => $query->fromCollection('banner')) ->find(...);
手动处理文件上传
文件上传发生在模型的 created
或 updated
事件被触发时。如果您在静默地创建或更新模型,您可以调用 createUploads
或 updateUploads
方法来手动处理文件上传。
public function update(Request $request, Post $post) { $post->update($request->all()); // If the post did not change, the `updated` event won't be fired. // So, we need to manually call the `updateUploads` method. if (! $post->wasChanged()) { $post->updateUploads(); } }
重要
根据您的配置,如果上传过程失败,createUploads
将删除模型,而 updateUploads
将将其更新为其原始属性。
临时禁用文件上传
您可以通过调用静态方法 disableUpload
临时禁用文件上传。
public function update(Request $request, Post $post) { // Temporarily disable the file uploads Post::disableUpload(); $post->update($request->all()); // Do more stuff here... // Manually process the uploads after everything you want to do $post->updateUploads(); }
注意事项
当您尝试创建或更新多个模型时,默认行为是上传请求中的所有文件,并将它们附加到所有这些模型上。这是因为在这些模型中,它们正在触发 created
或 updated
事件,从而触发上传过程。
有多种方法可以防止这种情况发生,例如
- 静默创建或更新模型。这样做,
created
或updated
事件不会被触发,这不会启动上传过程。如果你为这两个事件有模型观察者,这可能不是你想要的。 - 通过调用
disableUpload()
方法来禁用特定模型的上传过程。 - 从
NadLambino\Uploadable\Actions\Upload
动作本身禁用上传过程。Upload::disableFor()
方法可以接受一个模型类名、一个模型实例或一个包含每个或两者的数组。以下是一个示例
use NadLambino\Uploadable\Actions\Upload; public function store(Request $request) { // Disable the uploads for all of the instances of Post model Upload::disableFor(Post::class); // Files will be uploaded for User model User::create(...); // Files won't be uploaded for Post model Post::create(...); } // OR public function update(Request $request, User $user) { // Disable the uploads only for this specific $user Upload::disableFor($user); // Files won't be uploaded for this specific $user $user->update($request->validated()); $anotherUser = User::find(...); // Files will be uploaded for this $anotherUser $anotherUser->update(...); }
- 最后,通过调用
Upload::onlyFor
方法,具体指示NadLambino\Uploadable\Actions\Upload
只处理特定给定的模型。此方法与Upload::disableFor
具有相同的参数签名,并确保仅处理这些给定的模型类或实例。
use NadLambino\Uploadable\Actions\Upload; public function store() { // Process the uploads only for all of the instances of User model Upload::onlyFor(User::class); // Files will be uploaded for this User model User::create(...); // Files won't be uploaded for this Post model Post::create(...); } public function update(User $user) { // Process the uploads only for this specific $user Upload::onlyFor($user); // Files will be uploaded for this specific $user $user->update(...); $anotherUser = User::find(...); // Files won't be uploaded for this $anotherUser $anotherUser->update(...) }
此外,如果需要从禁用列表中删除模型,还有一个 NadLambino\Uploadable\Actions\Upload::enableFor()
方法。它与 onlyFor
的不同之处在于,onlyFor
方法确保文件只上传到给定的模型,而 enableFor
只简单地从禁用列表中删除给定的模型。
所有这些方法在队列上传时也可以正常工作。
注意
调用 disableFor
方法时,将从 onlyFor
模型列表中删除给定的模型。同样,当调用 onlyFor
方法时,它将从禁用模型列表中删除给定的模型。
在模型更新时上传文件
默认情况下,当你更新模型时,请求中的文件将添加到现有上传的文件中。如果你想用新的文件替换现有文件,你可以在 uploadable.php
配置文件中配置它。
'replace_previous_uploads' => true,
或者,你可以在更新模型之前调用静态方法 replacePreviousUploads
。
public function update(Request $request, Post $post) { // Replace the previous uploads Post::replacePreviousUploads(); $post->update($request->all()); }
注意
删除之前上传的过程只会在新文件成功上传时发生。
上传非请求文件的文件
如果你想上传一个不是直接来自请求的文件,你可以通过调用 uploadFrom
方法来实现。此方法可以接受一个 Illuminate\Http\UploadedFile
实例或一个数组、一个文件在 temporary_disk
上的字符串路径。
// DO $post->uploadFrom($request->file('image')); // OR $post->uploadFrom(new UploadedFile(...)); // OR $post->uploadFrom([ $request->file('image'), $request->file('avatar') ]); // OR $fullpath = ... // The path of the file that is uploaded in your `temporary_disk`. This could be something like an image that was modified by `ImageIntervention` then temporarily stored before uploading $post->uploadFrom($fullpath); // OR $post->uploadFrom([ $fullpath1, $fullpath2 ]); // OR even a mixed of both $post->uploadFrom([ $request->file('image'), $fullpath, ]); $post->save();
重要
确保你已验证了你传递到这里的文件,因为它在直接从请求上传时不会运行任何验证。
关系方法
已经预先定义了特定上传类型的关系方法。
// Relation for all types of uploads public function upload(): MorphOne { } // Relation for all types of uploads public function uploads(): MorphMany { } // Relation for uploads where extension or type is in the accepted image mimes public function image(): MorphOne { } // Relation for uploads where extension or type is in the accepted image mimes public function images(): MorphMany { } // Relation for uploads where extension or type is in the accepted video mimes public function video(): MorphOne { } // Relation for uploads where extension or type is in the accepted video mimes public function videos(): MorphMany { } // Relation for uploads where extension or type is in the accepted document mimes public function document(): MorphOne { } // Relation for uploads where extension or type is in the accepted document mimes public function documents(): MorphMany { }
重要
MorphOne 关系方法在查询中设置一个限制为一的值。
生命周期和事件
在整个上传文件的过程中,每个步骤都会触发事件。如果你需要在这些步骤之间做某事或仅用于调试目的,这将非常有帮助。
如果你想在上传信息存储到 uploads
表之前做某事,你可以在你的模型中定义一个公开的 beforeSavingUpload
方法。此方法将在文件上传到存储并在数据库中保存文件信息之前被调用。
public function beforeSavingUpload(Upload $upload, Model $model) : void { $upload->additional_field = "some value"; }
或者,你可以静态调用 beforeSavingUploadUsing
方法并传递一个闭包。闭包将接收与 beforeSavingUpload
方法相同的参数。只需确保在创建或更新模型之前调用此方法。此外,beforeSavingUploadUsing
优先级高于 beforeSavingUpload
,允许你在需要时覆盖它。
Post::beforeSavingUploadUsing(function (Upload $upload, Post $model) use ($value) { $model->additional_field = $value; }); $post->save();
重要
请记住,当您在队列中时,实际上是在不同的应用程序实例中运行上传进程,因此您无法访问当前应用程序状态,如请求对象。另外,请确保传递给 beforeSavingUploadUsing
方法的闭包及其依赖项是可序列化的。
排队
您可以通过在配置中定义队列名称来排队文件上传进程。
'upload_on_queue' => null,
或者,您还可以调用静态方法 uploadOnQueue
。
Post::uploadOnQueue('default'); $post->save();
测试
composer test
变更日志
请参阅 变更日志 了解最近有哪些更改。
贡献
请参阅 贡献指南 了解详细信息。
安全漏洞
请查阅我们关于如何报告安全漏洞的 安全策略。
鸣谢
许可证
MIT 许可证(MIT)。请参阅 许可文件 获取更多信息。