Laravel 文件数组总大小验证:使用自定义规则实现

laravel 文件数组总大小验证:使用自定义规则实现

本教程详细介绍了如何在 Laravel 中验证上传文件数组的整体总大小,而非仅限制单个文件大小。通过创建并实现一个自定义验证规则,我们将能够计算所有上传文件的总字节数,并与预设的最大限制进行比较,从而确保文件集合符合业务要求,提供更灵活的文件上传验证机制。

1. 理解问题:文件数组总大小验证的挑战

在 Laravel 中处理文件上传时,如果用户上传的是多个文件(作为一个数组),我们通常会使用 images.* 这样的规则来验证每个文件的类型和大小。例如 images.*’ => ‘mimes:jpg,jpeg,png|max:1000’ 会确保每个图片都是 JPG/PNG 格式且不超过 1000 KB。然而,Laravel 内置的验证规则无法直接对整个文件数组的总大小进行限制。例如,即使单个文件限制为 1MB,用户上传 100 个文件也可能导致总大小过大,超出服务器或存储系统的承受能力。

为了解决这一限制,我们需要一种机制来遍历文件数组,计算所有文件的总大小,并将其与一个预设的阈值进行比较。Laravel 强大的自定义验证规则功能正是为此类复杂场景设计的。

2. 创建自定义验证规则

首先,我们需要使用 Artisan 命令来生成一个新的自定义验证规则。假设我们希望将规则命名为 ArraySize。

php artisan make:rule ArraySize
登录后复制

这会在 app/Rules 目录下创建一个名为 ArraySize.php 的文件。

3. 实现 ArraySize 规则逻辑

打开 app/Rules/ArraySize.php 文件,并按照以下方式修改其内容。

<?php

namespace App/Rules;

use Illuminate/Contracts/Validation/Rule;
use Illuminate/Http/UploadedFile; // 引入 UploadedFile 类

class ArraySize implements Rule
{
    /**
     * 数组的最大允许总大小(单位:KB)。
     *
     * @var int
     */
    private int $maxSizeInKb;

    /**
     * 创建一个新的规则实例。
     *
     * @param int $maxSizeInKb 允许的最大总大小(KB)
     * @return void
     */
    public function __construct(int $maxSizeInKb)
    {
        $this->maxSizeInKb = $maxSizeInKb;
    }

    /**
     * 判断验证规则是否通过。
     *
     * @param  string  $attribute 正在验证的属性名
     * @param  mixed  $value 正在验证的属性值(文件数组)
     * @return bool
     */
    public function passes($attribute, $value): bool
    {
        $totalBytes = 0;

        // 确保 $value 是一个数组
        if (!is_array($value)) {
            return false;
        }

        foreach ($value as $file) {
            // 确保数组中的每个元素都是 UploadedFile 实例
            if (!$file instanceof UploadedFile) {
                // 如果不是上传文件实例,则验证失败,避免潜在错误
                return false;
            }
            // 累加每个文件的字节大小
            $totalBytes += $file->getSize();
        }

        // 将总字节数转换为 KB,并与设定的最大值进行比较
        // 注意:getSize() 返回的是字节数
        return ($totalBytes / 1024) <= $this->maxSizeInKb;
    }

    /**
     * 获取验证失败时返回的错误消息。
     *
     * @return string
     */
    public function message(): string
    {
        return sprintf('所有上传文件的总大小必须小于 %d KB。', $this->maxSizeInKb);
    }
}
登录后复制

代码解析:

  • __construct(int $maxSizeInKb): 构造函数接收一个整数参数 $maxSizeInKb,用于设定允许的最大总大小(单位为 KB)。这使得规则可以复用,并根据需要设置不同的限制。
  • passes($attribute, $value): 这是核心逻辑所在。

    • 它首先检查 $value 是否确实是一个数组。如果不是,则验证失败。
    • 然后,它遍历 $value 数组中的每个元素。
    • 对于每个元素,它检查是否为 Illuminate/Http/UploadedFile 实例。这确保我们正在处理的是实际上传的文件,并能安全地调用其方法。
    • $file->getSize() 方法用于获取单个文件的字节大小,并将其累加到 $totalBytes 变量中。
    • 最后,它将 $totalBytes 转换为 KB ($totalBytes / 1024),并与构造函数中传入的 $maxSizeInKb 进行比较。如果总大小小于或等于允许的最大值,则验证通过 (true);否则,验证失败 (false)。
  • message(): 当 passes() 方法返回 false 时,此方法将返回用户友好的错误消息。sprintf 函数用于动态地将最大允许大小插入到消息中。

4. 在表单请求或控制器中使用自定义规则

现在,我们可以在 Laravel 的表单请求 (Form Request) 或控制器中的验证逻辑中应用这个自定义规则。

首先,确保在文件中引入 ArraySize 规则:

use App/Rules/ArraySize;
// ... 其他 use 语句
登录后复制

然后,在 rules() 方法中添加对 images 字段的 ArraySize 规则。注意,images.* 规则仍然用于验证单个文件的属性(如类型和单个文件大小),而 images 规则则用于验证整个文件数组的总大小。

// ...
public function rules()
{
    return [
        'title'               => 'required|max:125',
        'quantity'            => 'required|numeric|min:0',
        'retail_price'        => 'required|numeric|min:0',
        'diamond_shape_id'    => 'required',
        'diamond_cut_id'      => 'required',
        'diamond_color_id'    => 'required',
        'diamond_clarity_id'  => 'required',
        'carat_weight'        => 'required',
        'diamond_polish_id'   => 'required',
        'diamond_symmetry_id' => 'required',
        'video_url'           => ['url', new EmbeddableUrl], // 假设 EmbeddableUrl 是另一个自定义规则
        'images.*'            => 'mimes:jpg,jpeg,png|max:1000', // 验证每个文件不超过 1000KB (1MB)
        'images'              => [new ArraySize(30000)],      // 验证所有文件总大小不超过 30000KB (即 30MB)
    ];
}
登录后复制

在上述示例中,new ArraySize(30000) 表示我们希望所有上传图片的总大小不超过 30000 KB (即 30 MB)。

5. 注意事项与最佳实践

  • 单位统一: 在自定义规则中,UploadedFile::getSize() 返回的是字节数。在构造函数中传入的 $maxSizeInKb 是 KB。务必在 passes 方法中进行正确的单位转换 ($totalBytes / 1024),以避免混淆和错误。
  • 错误消息定制: message() 方法允许你返回完全自定义的错误消息。可以根据应用程序的国际化需求,将其存储在语言文件中,例如 lang/xx/validation.php。
  • 结合其他规则: 自定义规则可以与 Laravel 内置的其他验证规则(如 mimes、max 等)无缝结合,共同提供全面的验证。images.* 确保了单个文件的基本属性,而 images 规则则关注集合的整体属性。
  • 健壮性考虑: 在 passes 方法中,对 $value 是否为数组以及数组元素是否为 UploadedFile 实例的检查,增强了规则的健壮性,避免在接收到非预期输入时发生错误。
  • 性能考量: 对于极大量的文件上传,计算总大小可能涉及 I/O 操作,但对于一般应用场景,其性能开销通常可以忽略不计。

总结

通过创建自定义验证规则 ArraySize,我们成功解决了 Laravel 中验证上传文件数组总大小的挑战。这种方法不仅提供了精确的控制,还保持了 Laravel 验证系统的优雅和可扩展性。掌握自定义规则的创建和使用,是开发复杂 Laravel 应用时一项非常有用的技能,它能帮助你处理各种非标准或业务特定的验证需求,从而构建更健壮、更符合业务逻辑的应用程序。

以上就是Laravel 文件数组总大小验证:使用自定义规则实现的详细内容,更多请关注php中文网其它相关文章!

https://www.php.cn/faq/1830460.html

发表回复

Your email address will not be published. Required fields are marked *