Ne-Lexa / php-zip

PhpZip is a php-library for extended work with ZIP-archives.
MIT License
491 stars 60 forks source link

Invalid zip file. The end of the central directory could not be found exception #95

Closed rudischenck closed 1 year ago

rudischenck commented 1 year ago
Q A
Library version(s) affected: ^4.0
PHP version(s): 8.1.17
OS (with bit depth): Ubuntu 20.04.6 LTS x86_64

Description

Using the code snippet below to export a directory

$zip = new ZipFile();
$zip->addDirRecursive($directory);
$zip->saveAsFile($zipFileName);

Sometimes, when I call ZipFile::openFile() on a file exported in this manner, the exception Invalid zip file. The end of the central directory could not be found. is thrown.

I've exported an object that fails without including the images I need and it works as expected. When the images are included in the directory it does not work. Bash also cannot open the .zip file, the output is in the following screenshot - image

The .zip file can be opened and its contents extracted normally in Windows 10. Outside of corrupted images (maybe?) I have no idea what might be causing it.

I've also attached the .zip file in question.

How to reproduce

The code below is somewhat simplified, I'm exporting objects/models, but these snippets should give the jist.

Download a zip

$zip = new ZipFile();
$zip->addDirRecursive($directory);
$zip->saveAsFile($zipFileName);
$zip->close();
$zipString = file_get_contents($zipFileName);
header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$trimmed_name_dir.zip\"");
// send zip to client
exit($zipString);

reupload and open the zip

$zip_file_path = "$tmpDir/{$_FILES['files']['name'][0]}";
move_uploaded_file($_FILES['files']['tmp_name'][0], $zip_file_path);
$zip = new ZipFile();
$zip->openFile($zip_file_path);

Possible Solution

I've tried using $zip->outputAsString(), didn't work. I've tried saving to a file using $zip->saveAsStream(), didn't work ($zip->saveAsStream() seems counter-productive given it closes the stream). I've tried using $zip->outputAsAttachment(), didn't work.

No idea. I'm lost. I've been working on it for the past 5 hours and haven't gotten anywhere.

Additional context

Zip file attached.

What_You_Can_Do_to_Help_Secure_the_Authentication_System.zip

odan commented 1 year ago

The exit method expects an int for the console. https://www.php.net/manual/en/function.exit.php

So this will not work:

// send zip to client
exit($zipString);

You may try the readfile function instead:

$zip = new ZipFile();
$zip->addDirRecursive($directory);
$zip->saveAsFile($zipFileName);
$zip->close();

header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$trimmed_name_dir.zip\"");
// send zip to client
readfile($zipFileName);
exit;

https://www.php.net/manual/en/function.readfile.php

rudischenck commented 1 year ago

The exit method expects an int for the console. https://www.php.net/manual/en/function.exit.php

So this will not work:

// send zip to client
exit($zipString);

You may try the readfile function instead:

$zip = new ZipFile();
$zip->addDirRecursive($directory);
$zip->saveAsFile($zipFileName);
$zip->close();

header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$trimmed_name_dir.zip\"");
// send zip to client
readfile($zipFileName);
exit;

https://www.php.net/manual/en/function.readfile.php

Unfortunately, this did not work either. I did find a specific instance where I removed an animated .gif file from the directory targeted for compression, the library was able to open the .zip file using ZipFile::openFile().

rudischenck commented 1 year ago

In my previous post I omitted the code that gets and saves the images to the target directory. The code is below.

foreach ($this->localizations as $localization) {
    preg_match_all('/<img[^>]+>/i', $localization->get('body'), $imgTags);
    foreach ($imgTags[0] as $img) {
        preg_match('/src="([^"]+)/i', $img, $imgSrc);
        $imgSrc = $imgSrc[1];
        if ($imgSrc && !in_array($imgSrc, $imageLinks)) {
            $imgData =  file_get_contents($imgSrc);
            if (!$imgData) {
                continue;
            }
            $imageLinks[] = $imgSrc;
            $imgName = basename($imgSrc);
            file_put_contents($directory . $imgName, $imgData);
        }
    }
}
rudischenck commented 1 year ago

Found the issue. The uploads were chunked and I wasn't reading all of them before trying to unzip the file.