Laravel 多文件下载教程:使用 ZipArchive 打包并提供下载
在很多业务场景中,我们需要让用户一次性下载多个文件,比如用户上传的多个附件、批量导出的报表文件等。如果逐个让用户下载,体验会非常差,最好的方式是将这些文件打包成一个压缩包,再提供单个下载链接。在 Laravel 框架中,我们可以借助 PHP 自带的 ZipArchive 扩展来实现这个功能,下面我们就来一步步实现这个需求。
环境准备
首先确保你的 PHP 环境已经开启了 ZipArchive 扩展,一般主流的 PHP 环境默认会开启这个扩展,你可以在 Laravel 项目中通过以下代码检查是否可用:
if (class_exists('ZipArchive')) {
echo 'ZipArchive 扩展已开启';
} else {
echo '请先开启 ZipArchive 扩展';
}如果提示扩展未开启,可以根据你的服务器环境修改 php.ini 配置文件,去掉 zip 扩展前的注释,重启服务即可。
实现思路
整个功能的实现流程可以分为以下几个步骤:
- 收集需要打包的多个文件路径,可以是本地存储的文件,也可以是用户上传的文件
- 创建一个临时的 zip 压缩包文件
- 遍历所有需要打包的文件,逐个添加到 zip 压缩包中
- 设置响应头,将 zip 文件返回给用户下载
- 下载完成后清理临时生成的 zip 文件,避免占用服务器存储空间
代码示例
下面是一个完整的 Laravel 控制器方法示例,实现了多文件打包下载的功能:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use ZipArchive;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Response;
class FileDownloadController extends Controller
{
/**
* 打包多个文件为zip并提供下载
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function downloadMultipleFiles(Request $request)
{
// 1. 获取需要打包的文件路径,这里示例用固定的文件,实际可以改为从请求或数据库获取
// 假设文件存储在 storage/app/public 目录下
$filePaths = [
storage_path('app/public/file1.pdf'),
storage_path('app/public/file2.docx'),
storage_path('app/public/file3.jpg'),
];
// 过滤不存在的文件,避免打包出错
$validFiles = [];
foreach ($filePaths as $file) {
if (file_exists($file)) {
$validFiles[] = $file;
}
}
if (empty($validFiles)) {
return response()->json(['message' => '没有可下载的文件'], 404);
}
// 2. 生成临时zip文件路径和名称
$zipFileName = '打包文件_' . time() . '.zip';
$zipPath = storage_path('app/temp/' . $zipFileName);
// 确保临时目录存在
if (!file_exists(storage_path('app/temp'))) {
mkdir(storage_path('app/temp'), 0755, true);
}
// 3. 初始化ZipArchive并添加文件
$zip = new ZipArchive();
if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
return response()->json(['message' => '创建压缩包失败'], 500);
}
foreach ($validFiles as $file) {
// 获取文件名,作为压缩包内的文件名称
$fileName = basename($file);
// 将文件添加到压缩包中,第二个参数是压缩包内的文件路径
$zip->addFile($file, $fileName);
}
// 关闭ZipArchive,完成压缩包写入
$zip->close();
// 4. 检查压缩包是否生成成功
if (!file_exists($zipPath)) {
return response()->json(['message' => '压缩包生成失败'], 500);
}
// 5. 返回下载响应,并设置下载完成后删除临时文件
return response()->download($zipPath, $zipFileName)->deleteFileAfterSend(true);
}
}上面的代码中,我们首先定义了需要打包的文件路径,这里使用的是 storage 目录下的文件,实际使用中你可以根据需求修改文件来源。然后创建临时 zip 文件,通过 ZipArchive 的 addFile 方法逐个把文件加入压缩包,最后通过 Laravel 的响应 download 方法返回给用户,同时设置 deleteFileAfterSend 为 true,确保用户下载完成后自动删除服务器上的临时 zip 文件,避免存储浪费。
路由配置
完成控制器方法后,需要在路由文件中配置对应的访问路由,这里以 web.php 为例:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\FileDownloadController;
Route::get('/download-multiple-files', [FileDownloadController::class, 'downloadMultipleFiles']);配置完成后,访问 /download-multiple-files 路径,就可以触发多文件打包下载的流程了。
注意事项
- 如果打包的文件数量很多或者单个文件体积很大,建议添加异步处理或者进度提示,避免请求超时
- 如果文件存储在云存储(如 OSS、S3)中,需要先下载到本地临时目录再打包,或者确认 ZipArchive 是否支持直接读取云存储文件
- 可以对压缩包的大小做限制,避免生成过大的文件导致服务器内存或存储不足
- 如果涉及用户私有文件,需要提前做好权限校验,确保用户只能下载自己有权限的文件