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

PHP创建大体积Zip文件后下载:如何确保压缩完成再触发下载?

问题描述

已实现PHP中Zip文件的创建与下载,但处理大于200MB的大Zip文件时,PHP会在压缩完成前就开始下载。压缩速度正常,尝试无压缩存储也未解决问题。

现有代码

创建Zip文件的函数

function makeRouteArchive($routeID) {
    // Get real path for our folder
    $rootPath = self::get_location($routeID);
    $relRootPath = $rootPath;
    while (!scandir($relRootPath)) {
        $relRootPath = '../'.$relRootPath;
    }
    $info = new SplFileInfo($relRootPath);
    $rootlen = strlen( $info->getRealPath());
    $zippath = $this->archive_root;
    while (!scandir($zippath)){
        $zippath = '../'.$zippath;
    }
    $routeText = 'oute'.sprintf('%07d',$routeID);
    $zipfilename = 'route'.sprintf('%07d',$routeID).'.zip';
    $zipfile = $zippath.$zipfilename;

    // Initialize archive object
    $zip = new ZipArchive();


    // Create recursive directory iterator
    /** @var SplFileInfo[] $files */
    $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($relRootPath), RecursiveIteratorIterator::SELF_FIRST );
    
    $zip->open($zipfile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
    foreach ($files as $name => $file)
    {
        // Skip directories (they would be added automatically)
        if (!$file->isDir())
        {
            // Get real and relative path for current file
            $filePath = $file->getRealPath();
            $relativePath = substr($filePath, $rootlen + 1);
            if (!strpos($relativePath,$routeText) === false) {
                if (strpos($relativePath,'Picture_') > 0) {
                    $relativePath = str_replace('Picture_','Pictures/',$relativePath);
                    $relativePath = 'Storage Card/RoutAbel/'.$relativePath;
                } elseif (strpos($relativePath,'Media_') > 0) {
                    $relativePath = str_replace('Media_','Media/',$relativePath);
                    $relativePath = 'Storage Card/RoutAbel/'.$relativePath;
                } elseif (strpos($relativePath,'ARobjects_') > 0) {
                        $relativePath = str_replace('ARobjects_','ARobjects/',$relativePath);
                    $relativePath = 'Storage Card/RoutAbel/'.$relativePath;
                } elseif (strpos($relativePath,'_') == 12 )  {
                    $relativePath = 'Storage Card/RoutAbel/'.substr_replace($relativePath,'/Content/',12,1);
                    $relativePath = str_replace('_Marker','/Content/Marker', $relativePath);
                    $relativePath = str_replace('_Slider','/Content/Slider', $relativePath);
                }
// Add current file to archive
                $zip->addFile($filePath, $relativePath);
            }
        }
    }

    // Zip archive will be created only after closing object
    $zip->close();

    return $result;
}

主代码

$zipfile = makeRouteArchive($routeID);


if (headers_sent()) {
    echo 'HTTP header already sent';
} else {
    if (!is_file($zipfile)) {
        header($_SERVER['SERVER_PROTOCOL'].' 404 Not Found');
        echo 'File not found';
    } else if (!is_readable($zipfile)) {
        header($_SERVER['SERVER_PROTOCOL'].' 403 Forbidden');
        echo 'File not readable';
    } else {
        while (ob_get_level()) {
            ob_end_clean();
        }
        ob_start();
        header($_SERVER['SERVER_PROTOCOL'].' 200 OK');
        header("Content-Type: application/zip");
        header("Content-Transfer-Encoding: Binary");
        header("Content-Length: ".filesize($file));
        header('Pragma: no-cache');
        header("Content-Disposition: attachment; filename=\"".basename($zipfile)."\"");
        ob_flush();
        ob_clean();
        readfile($zipfile);
       exit;
    }
}
解决方案

1. 修复Zip函数的返回值错误

原函数最后返回的$result从未定义,导致主代码中$zipfile拿到的是无效值,小文件场景下缓冲机制可能掩盖问题,大文件则会提前触发下载流程。将函数末尾的:

return $result;

改为:

return $zipfile;

2. 修正响应头中的文件长度变量

主代码中filesize($file)里的$file未定义,导致Content-Length头错误,改为:

header("Content-Length: ".filesize($zipfile));

3. 优化输出缓冲逻辑

确保发送响应头前彻底清空所有输出缓冲,避免提前输出内容触发下载,调整主代码中的缓冲处理部分:

// 彻底清空所有输出缓冲
while (ob_get_level()) {
    ob_end_clean();
}

// 直接发送响应头,无需再次启动缓冲
header($_SERVER['SERVER_PROTOCOL'].' 200 OK');
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: Binary");
header("Content-Length: ".filesize($zipfile));
header('Pragma: no-cache');
header("Content-Disposition: attachment; filename=\"".basename($zipfile)."\"");

// 直接输出文件内容
readfile($zipfile);
exit;

4. 增加大文件处理的超时设置

在makeRouteArchive函数开头添加超时设置,避免压缩大文件时脚本被终止:

function makeRouteArchive($routeID) {
    // 取消脚本超时限制(需确保服务器允许)
    set_time_limit(0);
    // 剩余原有代码...
}

5. 验证Zip文件生成状态

在$zip->close()后添加状态检查,确保压缩完成且无错误:

$zip->close();

// 检查Zip是否生成成功
if ($zip->status !== ZipArchive::ER_OK) {
    // 处理错误,比如返回false或抛出异常
    return false;
}

return $zipfile;

同时在主代码中增加对返回值的判断:

$zipfile = makeRouteArchive($routeID);

if (!$zipfile) {
    header($_SERVER['SERVER_PROTOCOL'].' 500 Internal Server Error');
    echo 'Failed to create zip archive';
    exit;
}

// 剩余原有代码...

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 20:12:49