nadlambino/uploadable

自动处理模型文件上传。

v1.2.0 2024-07-23 11:35 UTC

This package is auto-updated.

Last update: 2024-09-23 12:03:17 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

目录

安装

您可以通过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. 文件名和上传路径

您可以通过在模型中定义公共方法 getUploadFilenamegetUploadPath 来自定义文件名和路径。

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(...);

手动处理文件上传

文件上传发生在模型的 createdupdated 事件被触发时。如果您在静默地创建或更新模型,您可以调用 createUploadsupdateUploads 方法来手动处理文件上传。

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();
}

注意事项

当您尝试创建或更新多个模型时,默认行为是上传请求中的所有文件,并将它们附加到所有这些模型上。这是因为在这些模型中,它们正在触发 createdupdated 事件,从而触发上传过程。

有多种方法可以防止这种情况发生,例如

  • 静默创建或更新模型。这样做,createdupdated 事件不会被触发,这不会启动上传过程。如果你为这两个事件有模型观察者,这可能不是你想要的。
  • 通过调用 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)。请参阅 许可文件 获取更多信息。