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

Laravel集成MinIO处理大文件压缩下载内存溢出问题求助

Laravel + MinIO 大文件压缩下载内存溢出解决方案

问题描述

我正在通过Laravel控制器下载文件夹,但因其中一个文件约500MB,出现了“内存溢出”问题。目前我采用的方式是从MinIO存储下载每个文件后再进行压缩,但受内存限制,这种方式无法适配所有场景。请问是否有以下两种解决方案:

  1. 直接从MinIO下载已压缩的文件夹(我未在文档中找到相关方法);
  2. 将MinIO中的文件直接流式传输到压缩包中以降低内存占用?

当前实现代码:

private function getDescendents(
    FileOrFolder $folder,
    ZipArchive &$zip,
    string $path
) : void {
    foreach ($folder->childrens as $children) {
        $this->addDescendent($children, $zip, $path);
    }
}

private function addDescendent(
    FileOrFolder $fileSystemItem,
    ZipArchive $zip,
    string $path
) : void {

    $this->pushInZip($fileSystemItem, $zip, $path);

    if($fileSystemItem->type == FileOrFolderType::Folder) {
        $path .= $fileSystemItem->name . '/';
        $this->getDescendents($fileSystemItem, $zip, $path);
    }
}

private function pushInZip(
    FileOrFolder $fileSystemItem,
    ZipArchive $zip,
    string $path
) : void {

    $minioStorage = Storage::disk('minio');

    if($fileSystemItem->type == FileOrFolderType::Folder) {
        $zip->addEmptyDir($path . $fileSystemItem->name);
        return;
    }

    $fullPath = PathBuilder::buildFullPath($fileSystemItem);
    $fileStream = $minioStorage->readStream($fullPath);
    $filePathForZip = $path . $fileSystemItem->name;

    $zip->addFromString($filePathForZip, stream_get_contents($fileStream));
}

解决方案

针对你的两个疑问

  • 直接从MinIO下载已压缩的文件夹:MinIO没有原生的文件夹压缩下载功能,无法直接调用接口实现。如果需要这种能力,要么在文件上传时预先压缩存储,要么在服务端自行处理压缩逻辑。
  • 流式传输到压缩包降低内存占用:这是完全可行的,核心是避免一次性将大文件读入内存,而是分块读取MinIO的文件流,直接写入压缩流并输出到客户端,全程内存占用仅为单块数据的大小。

推荐实现方案(流式压缩)

使用League\ZipStream库替代原生ZipArchive,它专门针对流式压缩场景设计,无需本地临时文件,直接将压缩流输出到HTTP响应。

步骤1:安装依赖

composer require league/zipstream

步骤2:重构控制器代码

use League\ZipStream\ZipStream;
use Illuminate\Support\Facades\Storage;
use League\Flysystem\MountManager;

public function downloadFolder(FileOrFolder $folder)
{
    // 初始化ZipStream,设置压缩包名称
    $zip = new ZipStream($folder->name . '.zip');

    // 挂载MinIO存储驱动,方便流式读取
    $manager = new MountManager([
        'minio' => Storage::disk('minio')->getDriver(),
    ]);

    // 递归处理文件夹内的所有文件和子文件夹
    $this->streamItemToZip($folder, $zip, $manager, '');

    // 完成压缩并输出到客户端
    $zip->finish();
}

private function streamItemToZip(FileOrFolder $item, ZipStream $zip, MountManager $manager, string $basePath)
{
    $currentPath = $basePath . $item->name;

    if ($item->type === FileOrFolderType::Folder) {
        // 向压缩包添加空文件夹
        $zip->addEmptyDir($currentPath . '/');
        
        // 递归处理子项
        foreach ($item->childrens as $child) {
            $this->streamItemToZip($child, $zip, $manager, $currentPath . '/');
        }
        return;
    }

    // 流式读取MinIO文件并写入压缩包
    $fullMinioPath = PathBuilder::buildFullPath($item);
    
    $manager->readStream('minio://' . $fullMinioPath, function ($stream) use ($zip, $currentPath) {
        // 分块传输,内存占用仅为单次读取的块大小(默认8KB)
        $zip->addFileFromStream($currentPath, $stream);
    });
}

备选方案(原生ZipArchive优化)

如果必须使用原生ZipArchive,可以通过临时文件中转,避免一次性加载大文件到内存:

private function pushInZip(FileOrFolder $fileSystemItem, ZipArchive $zip, string $path)
{
    $minioStorage = Storage::disk('minio');

    if($fileSystemItem->type == FileOrFolderType::Folder) {
        $zip->addEmptyDir($path . $fileSystemItem->name);
        return;
    }

    $fullPath = PathBuilder::buildFullPath($fileSystemItem);
    $fileStream = $minioStorage->readStream($fullPath);
    $filePathForZip = $path . $fileSystemItem->name;

    // 创建临时文件分块写入
    $tempFile = tempnam(sys_get_temp_dir(), 'minio_zip');
    $tempStream = fopen($tempFile, 'wb');
    
    // 8KB分块读取,控制内存占用
    while (!feof($fileStream)) {
        fwrite($tempStream, fread($fileStream, 8192));
    }
    
    fclose($fileStream);
    fclose($tempStream);
    
    // 将临时文件添加到压缩包
    $zip->addFile($tempFile, $filePathForZip);
    
    // 删除临时文件
    unlink($tempFile);
}

内容的提问来源于stack exchange,提问作者vincent clerc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 23:39:54