You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Livewire异步多文件上传:进度条不显示及上传中无法添加新文件的问题排查与解决

Laravel Livewire异步多文件上传:进度条不显示及上传中无法添加新文件的问题排查与解决

嘿,我来帮你搞定这两个问题!先看你的代码,其实这俩问题都和Livewire的上传机制以及当前的实现逻辑有关,咱们一个个拆解解决:


问题1:进度条不显示

问题原因

你当前的进度条只是用了wire:loading做静态加载提示,没有利用Livewire内置的上传进度跟踪功能;而且store方法里的sleep(1)会让整个请求同步阻塞,Livewire没法实时反馈真实的上传进度。

解决方案

咱们利用Livewire的WithFileUploads提供的进度监听钩子,实现真实的文件级进度跟踪:

1. 修改Livewire组件代码

<?php

namespace App\Livewire;

use App\Models\File;
use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\Storage;

class UploadPage extends Component
{
    use WithFileUploads;

    public $files = [];
    public $uploadProgress = []; // 新增:存储每个文件的上传进度

    // 监听Livewire的上传进度事件
    protected $listeners = [
        'fileUploadProgress' => 'updateUploadProgress'
    ];

    // 更新进度数据
    public function updateUploadProgress($name, $progress)
    {
        $this->uploadProgress[$name] = $progress;
    }

    public function removeFile($index)
    {
        array_splice($this->files, $index, 1);
    }

    public function store()
    {
        $this->validate([
            'files.*' => 'required|file|mimes:jpg,png,svg,mp4',
        ]);

        foreach ($this->files as $key => $file) {
            try {
                // 改用异步上传,避免阻塞组件响应
                $path = Storage::disk('r2')->putAsync('uploads', $file);

                File::create([
                    'name' => $file->getClientOriginalName(),
                    'type' => $file->getMimeType(),
                    'path' => $path,
                ]);

                // 上传完成后移除该文件,而非清空整个数组
                unset($this->files[$key]);
                unset($this->uploadProgress[$file->getFilename()]);
            } catch (\Exception $e) {
                session()->flash('error', "上传失败:{$file->getClientOriginalName()} - {$e->getMessage()}");
            }
        }

        // 重新索引数组,避免后续删除操作出现索引混乱
        $this->files = array_values($this->files);
        session()->flash('success', '已选文件上传完成!');
    }

    public function render()
    {
        return view('livewire.upload-page');
    }
}

2. 修改Blade视图的进度条部分

替换原来的全局进度条,改为显示每个文件的实时进度:

<!-- 替换原有的Progress Bar部分 -->
<!-- 单个文件进度条 -->
@if (!empty($uploadProgress))
    <div class="mt-6 space-y-4">
        <p class="text-sm font-semibold text-neutral-300">上传进度:</p>
        @foreach ($files as $index => $file)
            @if (isset($uploadProgress[$file->getFilename()]))
                <div class="bg-neutral-700 rounded-full h-2 overflow-hidden">
                    <div class="bg-lime-500 h-2 rounded-full" style="width: {{ $uploadProgress[$file->getFilename()] }}%"></div>
                </div>
                <p class="text-xs text-neutral-400">
                    {{ $file->getClientOriginalName() }}: {{ $uploadProgress[$file->getFilename()] }}%
                </p>
            @endif
        @endforeach
    </div>
@endif

<!-- 保留全局加载提示(可选) -->
<div class="mt-6">
    <p wire:loading wire:target="store" class="text-xs text-neutral-400 mt-2">正在处理上传,请稍候...</p>
</div>

3. 给文件输入添加进度监听

在文件input标签上加上wire:progress钩子:

<input type="file" wire:model="files" wire:progress="fileUploadProgress" multiple
       class="absolute inset-0 opacity-0 cursor-pointer z-10">

问题2:上传中无法添加新文件

问题原因

原来的store方法是同步执行的,而且上传完成后会清空整个$files数组;请求阻塞期间,Livewire组件处于"忙碌"状态,前端无法交互添加新文件。

解决方案

通过异步上传和保留未上传文件的方式,让组件在上传过程中保持可交互:

  1. 用putAsync替代put:异步上传不会阻塞组件,前端可以继续选择新文件
  2. 只移除已上传的文件:不再清空整个$files数组,未上传的文件会保留在列表中,用户可以继续添加新文件
  3. 重新索引数组:避免删除文件后出现索引断层,导致后续removeFile操作出错

另外,记得在前端保留文件选择区域,上传过程中它会保持可点击状态。


额外优化建议

  • 添加错误提示:在视图中加入错误消息展示,方便用户知道哪个文件上传失败
@if (session()->has('error'))
    <div class="p-4 text-red-500 bg-neutral-700 rounded-lg">
        {{ session('error') }}
    </div>
@endif
  • 限制文件大小:可以在验证规则里加上max:10240(限制10MB),避免超大文件导致超时

备注:内容来源于stack exchange,提问作者irfan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 03:20:16