如何在PHP中通过流式处理解压S3中的大Zip文件?
PHP流式解压S3中大型Zip文件的可行方案
PHP完全支持流式解压处理,不需要把整个Zip文件下载到本地或全量加载到内存,能边下载、边解压、边上传到S3,完美适配Lambda的资源限制,不用额外升级Vapor资源。
核心实现思路
主要依赖PHP的原生扩展或轻量第三方库,结合AWS SDK的流式API,实现"分段处理":
- 从S3流式拉取Zip文件的分片内容,而非一次性下载完整文件
- 解析Zip流,直接定位并读取内部的JSON文件流
- 将解压后的JSON流直接上传回S3,全程内存占用可控
原生PHP + AWS SDK 实现(Laravel环境)
这是最贴合Laravel Lambda场景的方案,无需额外引入第三方库:
use Aws\S3\S3Client; use ZipArchive; // 初始化S3客户端(Laravel环境也可直接用Storage门面) $s3 = app('aws')->createClient('s3'); // 配置源文件和目标文件的S3路径 $sourceBucket = 'your-source-bucket'; $sourceKey = 'path/to/large-archive.zip'; $targetBucket = 'your-target-bucket'; $targetKey = 'path/to/uncompressed.json'; // 创建临时内存流存储Zip分片内容(内存不足时自动转临时文件) $zipTempStream = fopen('php://temp', 'r+'); // 从S3流式拉取Zip文件,边拉边写入临时流 $s3->getObject([ 'Bucket' => $sourceBucket, 'Key' => $sourceKey, 'Callback' => function ($chunk) use ($zipTempStream) { fwrite($zipTempStream, $chunk); fflush($zipTempStream); }, ]); // 重置临时流指针到开头 rewind($zipTempStream); $zip = new ZipArchive(); // 通过zip://协议打开内存中的Zip流 if ($zip->open('zip://' . stream_get_meta_data($zipTempStream)['uri']) === true) { // 假设Zip内只有一个JSON文件,若有多个可遍历getNameIndex定位 $jsonEntryName = $zip->getNameIndex(0); // 获取Zip内JSON文件的流 $jsonStream = $zip->getStream($jsonEntryName); // 直接把JSON流上传到S3,无需落地本地 $s3->putObject([ 'Bucket' => $targetBucket, 'Key' => $targetKey, 'Body' => $jsonStream, ]); $zip->close(); } fclose($zipTempStream);
注意事项
- 确保Lambda执行角色拥有S3的
GetObject和PutObject权限 - 如果Zip内有多个文件,可通过循环
$zip->numFiles遍历getNameIndex找到目标JSON php://temp默认内存阈值是1MB,超过后会自动切换到Lambda的临时存储(/tmp),但流式处理下内存占用极低- 若用Laravel的
Storage门面,可替换S3客户端的初始化,代码更简洁
替代方案:PhpZip库(纯PHP实现)
如果原生ZipArchive在某些环境下有兼容性问题,可使用PhpZip纯PHP库,它对流式处理的支持更灵活:
use PhpZip\ZipFile; use Aws\S3\S3Client; $s3 = app('aws')->createClient('s3'); $zipTempStream = fopen('php://temp', 'r+'); // 先从S3拉取Zip到临时流(同上面的代码) $s3->getObject([ 'Bucket' => $sourceBucket, 'Key' => $sourceKey, 'Callback' => function ($chunk) use ($zipTempStream) { fwrite($zipTempStream, $chunk); fflush($zipTempStream); }, ]); rewind($zipTempStream); $zipFile = new ZipFile(); $zipFile->openStream($zipTempStream); // 遍历Zip内的文件,找到JSON文件 foreach ($zipFile->getEntries() as $entry) { if (pathinfo($entry->getName(), PATHINFO_EXTENSION) === 'json') { $jsonStream = $entry->getInputStream(); $s3->putObject([ 'Bucket' => $targetBucket, 'Key' => $targetKey, 'Body' => $jsonStream, ]); break; } } $zipFile->close(); fclose($zipTempStream);
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

