Laravel队列任务无法生成Zip文件问题求助
Laravel 10 队列批量任务后Zip打包失败问题
我使用Laravel 10的批量队列任务生成XML文件,文件会保存到指定文件夹。待所有XML文件生成完成后,通过CreateExportacaoEsusZip任务将这些文件打包为Zip并删除原文件夹。
XML文件及文件夹的生成均正常,但执行Zip打包任务时,看似运行正常却未生成Zip文件。同步执行(不使用QUEUE_CONNECTION=database)时功能正常,推测问题与后台队列执行有关:本地Windows 11环境下生成的Zip文件损坏,Linux服务器环境下则完全不生成Zip,且两种情况均无报错或警告信息。
任务调用代码
// 批量任务和Zip打包任务的调用代码 Bus::chain([ Bus::batch($jobs_batched), new CreateExportacaoEsusZip($fold['cryptFolder'], $fold['path'], $file_name), function () use ($data, $loteExportacao, $user, $file_name) { $loteExportacao->arquivo = "exportacoes_esus/$file_name.zip"; $loteExportacao->em_processamento = false; $loteExportacao->save(); $user->notify(new ExportFichasEsusNotification('after', $user)); Log::notice(logMessage('Arquivos XML E-sus', 'Geração de Arquivo XML', $user->id)); } ])->catch(function (Batch $batch, Throwable $e) use ($user, $fold) { if ($fold['cryptFolder']) { Storage::deleteDirectory("exportacoes_esus/{$fold['cryptFolder']}"); } $user->notify(new ExportFichasEsusNotification('error', $user)); Log::error(logMessage('Arquivos XML E-sus', 'Geração de Arquivo XML', $user->id), ['error' => $e->getMessage()]); })->dispatch();
CreateExportacaoEsusZip任务实现
<?php namespace App\Jobs; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use League\Flysystem\MountManager; use ZipArchive; class CreateExportacaoEsusZip implements ShouldQueue { use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; private string $folder; private string $path; private string $file_name; /** * Create a new job instance. */ public function __construct(string $folder, string $path, string $file_name) { $this->folder = $folder; $this->path = $path; $this->file_name = $file_name; } /** * Execute the job. */ public function handle(): void { try { $zip = new ZipArchive; $fileName = storage_path('app/exportacoes_esus') . '/' . $this->file_name . '.zip'; if (!File::exists($this->path)) { throw new \Exception("Directory does not exist: $this->path"); } if ($zip->open($fileName, ZipArchive::CREATE) === true) { $files = File::files($this->path); if (empty($files)) { throw new \Exception("No files found in directory: $this->path"); } foreach ($files as $value) { $relativeNameInZipFile = basename($value); $zip->addFile($value, $relativeNameInZipFile); } $zip->close(); Log::info("ZIP file created successfully: $fileName"); } else { throw new \Exception("Could not create zip file at $fileName"); } if (env('FILESYSTEM_DISK') == 's3') { $mountManager = new MountManager([ 'local' => Storage::disk('local')->getDriver(), 's3' => Storage::disk('s3')->getDriver(), ]); $mountManager->copy("local://exportacoes_esus/$this->file_name.zip", "s3://exportacoes_esus/$this->file_name.zip"); Storage::disk('local')->delete("exportacoes_esus/$this->file_name.zip"); Log::info("ZIP file copied to S3 and deleted locally: $this->file_name.zip"); } Storage::disk('local')->deleteDirectory("exportacoes_esus/$this->folder"); Log::info("Temporary directory deleted: $this->folder"); } catch (\Exception $e) { Log::error('Error creating ZIP file', [ 'folder' => $this->folder, 'path' => $this->path, 'file_name' => $this->file_name, 'error' => $e->getMessage(), ]); Storage::delete("exportacoes_esus/$this->file_name.zip"); throw $e; } } }
问题排查与解决方案
1. 队列进程的文件权限问题
Linux环境下,队列进程(如php artisan queue:work)的运行用户可能和Web服务器用户不一致,导致没有写入storage/app/exportacoes_esus目录的权限:
- 执行
ls -l storage/app/exportacoes_esus检查目录所属用户和权限; - 调整权限:
chmod -R 775 storage/app/exportacoes_esus,或设置正确用户组:chown -R www-data:www-data storage/app/exportacoes_esus(根据服务器实际用户调整)。
2. 路径一致性问题
队列进程的工作目录可能和Web请求环境不同,导致传递的$path失效:
- 避免传递相对路径,改用Laravel Storage获取绝对路径:在生成XML时,用
Storage::disk('local')->path("exportacoes_esus/{$fold['cryptFolder']}")获取路径并传递给任务; - 在任务的
handle方法中,重新通过Storage获取目录路径,替代构造函数传递的$path:
$actualPath = Storage::disk('local')->path("exportacoes_esus/{$this->folder}"); if (!File::exists($actualPath)) { throw new \Exception("Directory does not exist: $actualPath"); }
3. Windows下Zip文件损坏问题
Windows环境下,ZipArchive关闭后可能未立即释放文件句柄,导致文件损坏:
- 在
$zip->close()后添加强制内存回收:
$zip->close(); gc_collect_cycles(); Log::info("ZIP file created successfully: $fileName");
4. S3同步时机问题
如果使用S3,需确保本地Zip文件完全写入后再执行复制:
if (env('FILESYSTEM_DISK') == 's3') { // 等待文件写入完成 while (!File::exists($fileName)) { usleep(100000); // 等待100ms } // 后续S3复制逻辑不变 }
5. 强化日志调试
在任务中添加更多日志,确认每个步骤的执行状态:
- 在获取文件列表后添加:
Log::info('Files found for zipping', ['count' => count($files), 'files' => $files]); - 实时查看队列日志:
tail -f storage/logs/laravel.log,排查隐藏的错误信息。
内容的提问来源于stack exchange,提问作者Igor Morais
相关产品推荐
相关产品推荐

