Laravel使用ZipArchive生成zip下载多文件报文件不存在错误
错误原因
- 报错
ZipArchive::addFile(): No such file or directory的直接触发原因是:调用addFile方法时传入的目标文件路径在服务器上不存在,ZipArchive无法读取到要压缩的源文件。 - 常见的具体诱因:
- 数据库中记录的
filename与服务器实际存储的文件名不匹配,可能是文件被手动删除、文件名录入错误、文件名包含特殊字符/中文导致拼接后的路径乱码 - 文件存储路径配置错误:Laravel默认通过
store()方法上传的文件存放在storage/app目录下,并非public目录,直接拼接public_path会定位到错误路径 - 代码未做任何存在性校验,只要文件列表中有任意一个文件缺失,就会直接抛出致命错误中断整个流程
- 数据库中记录的
- 原代码还存在两个隐性问题:一是固定使用
documents.zip作为压缩包文件名,高并发场景下会出现文件读写覆盖冲突;二是生成的压缩包长期残留在public目录,既产生垃圾文件也有公开访问风险。
修复代码
use ZipArchive; use App\Models\Documents; use Illuminate\Support\Facades\Storage; public function download() { $zip = new ZipArchive; // 生成带随机串的临时压缩包文件名,避免并发冲突 $zipName = 'documents_' . time() . '_' . str_random(6) . '.zip'; $zipPath = storage_path('app/temp/' . $zipName); // 提前创建临时目录,避免目录不存在导致的写入失败 if (!is_dir(dirname($zipPath))) { mkdir(dirname($zipPath), 0755, true); } if ($zip->open($zipPath, ZipArchive::CREATE) === true) { foreach (Documents::cursor() as $document) { // 用游标遍历,避免文件数量大时内存溢出 // 按实际存储位置替换文件路径:如果文件存在storage/public目录,用Storage::disk('public')->path('documents/'.$document->filename) $sourcePath = public_path('uploads/documents/' . $document->filename); // 校验文件真实存在且可读,跳过缺失文件避免报错 if (is_file($sourcePath) && is_readable($sourcePath)) { // 转码处理Windows下中文文件名乱码问题,纯英文文件名可省略这步 $innerName = iconv('UTF-8', 'GBK//IGNORE', $document->filename); $zip->addFile($sourcePath, $innerName); } } $zip->close(); } // 输出下载,请求完成后自动删除临时压缩包,不残留文件 return response()->download($zipPath)->deleteFileAfterSend(true); }
配置校验要点
- 如果文件存储在
storage/app/public目录,先执行php artisan storage:link创建软链,同时将$sourcePath替换为对应磁盘的真实路径,不要硬编码public路径 - 如果服务器开启了open_basedir限制,需要确认storage目录在允许访问的范围内,避免出现权限拒绝错误
- 当待压缩文件数量较多时,保持
Documents::cursor()的遍历写法,不要改回Documents::all(),防止内存占用过高。
内容的提问来源于stack exchange,提问作者jhon jhon
相关产品推荐
相关产品推荐

