
本教程详细介绍了如何在 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中文网其它相关文章!


