0

There are similar questions about creating and downloading zip files in PHP and I can do that. The problem is that some zip files are bigger (> 200 MB) and PHP does not wait until the zip file is complete before is starts downloading.

The speed of compressing is OK. I tried storing the files uncompressed is only marginally faster and is not solving the problem.

The function for creating the zip file looks like this:

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;
}

The main code is simply :

$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;
        }
    }

The code is working perfect, as long as there are not to many or to large files to be zipped.

My problem: How can I force the function to wait until the zip file is ready?

KIKO Software
  • 15,283
  • 3
  • 18
  • 33
  • I don't think this is a problem of PHP not waiting for the ZIP to be completed. PHP doesn't do the downloading anyway, it's the browser that does that. PHP just generates the output for the web server. My guess is that your maximum PHP execution time is reached, and PHP stops executing. The browser will then receive what was output by PHP up to that point. – KIKO Software Sep 01 '22 at 09:50
  • See for instance: https://stackoverflow.com/questions/5164930/fatal-error-maximum-execution-time-of-30-seconds-exceeded – KIKO Software Sep 01 '22 at 09:51
  • That's why most services send an email, when ready to download. – Markus Zeller Sep 01 '22 at 10:46
  • It is not the execution time. The zip file will be created fully and it is a valid zip file. The routine creating the file is returning the filename before the file is ready. Sending an email is no option, it is content for an app which waits for the answer. – user1886216 Sep 01 '22 at 11:53
  • Why do you send the headers before the file is rendered? – Markus Zeller Sep 01 '22 at 13:52
  • The question is how to wait for the file to be rendered before sending it. – user1886216 Sep 01 '22 at 19:57
  • As far as I can tell `makeRouteArchive()` is busy until the ZIP file is created. It doesn't spawn a separate process. That means you're not sending headers before the file is completed. No idea why Markus, and you, think that's the case. – KIKO Software Sep 01 '22 at 22:24
  • 1
    I think the real problem is `ob_start();`, because neither `ob_flush();` nor `ob_clean();` destroy the output buffer. So the problem is probably the output buffer. – KIKO Software Sep 01 '22 at 22:26
  • Ob_start() was the problem. Thanks a lot. – user1886216 Sep 02 '22 at 08:47

0 Answers0