maennchen / ZipStream-PHP

:floppy_disk: PHP ZIP Streaming Library
https://maennchen.dev/ZipStream-PHP/
MIT License
1.74k stars 104 forks source link

Unexpected end of archive - when there are more than 4 folders deep #296

Closed d1ca closed 8 months ago

d1ca commented 8 months ago

ZipStream-PHP version

3.0.1

PHP version

8.1

Constraints for Bug Report

Summary

When trying to add file(s) to a zip and the files are at a lower level within the folders (let say 5 or more), the zip is created but it cannot be opened. you get: Unexpected end of arhive.

Current behavior

When trying to add file(s) to a zip and the files are at a lower level within the folders (let say 5 or more), the zip is created but it cannot be opened. you get: Unexpected end of arhive. image

How to reproduce


// create a new zipstream object
$zip = new ZipStream\ZipStream(
    outputName: 'example.zip',

    // enable output of HTTP headers
    sendHttpHeaders: true,
);

// create a file named 'hello.txt'
$zip->addFile(
    fileName: 'some/other/folders/with/nothing/to/say/about/hello.txt',
    data: 'This is the contents of hello.txt',
);

// finish the zip stream
$zip->finish();

Expected behavior

create the zip with unlimited number of folders and subfolders no matter of their depth in order to allow files to be at the lowest subfolders.

maennchen commented 8 months ago

@d1ca I've tried to reproduce this using the following test:

    public function testAddDeeplyNestedFile(): void
    {
        $zip = new ZipStream(
            outputStream: $this->tempfileStream,
            sendHttpHeaders: false,
        );

        $zip->addFile('some/other/folders/with/nothing/to/say/about/hello.txt', 'Sample String Data');

        $zip->finish();

        $tmpDir = $this->validateAndExtractZip($this->tempfile);

        $files = $this->getRecursiveFileList($tmpDir);
        $this->assertSame(['some' . DIRECTORY_SEPARATOR . 'other' . DIRECTORY_SEPARATOR . 'folders' . DIRECTORY_SEPARATOR . 'with' . DIRECTORY_SEPARATOR . 'nothing' . DIRECTORY_SEPARATOR . 'to' . DIRECTORY_SEPARATOR . 'say' . DIRECTORY_SEPARATOR . 'about' . DIRECTORY_SEPARATOR . 'hello.txt'], $files);

        $this->assertStringEqualsFile($tmpDir . '/some/other/folders/with/nothing/to/say/about/hello.txt', 'Sample String Data');
    }

This seems to work fine on my machine and I can also open it using the default Linux unarchiver in the UI.

I therefore think that this is an issue with the unarchiver you're using. Can you try to use a different tool on the same file and see if that works?

d1ca commented 8 months ago

Hi,

First of all, thanks for replying so fast. Secondly, that's strange... If you have time and you can, i can show you the strange behavior. It's looks that the zip crashes when i simply make a simple function call (only get data from DB and iterates through them). If you can take a look, i can set up a google meeting.

Thanks, Have a nice day

În lun., 22 ian. 2024 la 11:39, Jonatan Männchen @.***> a scris:

@d1ca https://github.com/d1ca I've tried to reproduce this using the following test:

public function testAddDeeplyNestedFile(): void
{
    $zip = new ZipStream(
        outputStream: $this->tempfileStream,
        sendHttpHeaders: false,
    );

    $zip->addFile('some/other/folders/with/nothing/to/say/about/hello.txt', 'Sample String Data');

    $zip->finish();

    $tmpDir = $this->validateAndExtractZip($this->tempfile);

    $files = $this->getRecursiveFileList($tmpDir);
    $this->assertSame(['some' . DIRECTORY_SEPARATOR . 'other' . DIRECTORY_SEPARATOR . 'folders' . DIRECTORY_SEPARATOR . 'with' . DIRECTORY_SEPARATOR . 'nothing' . DIRECTORY_SEPARATOR . 'to' . DIRECTORY_SEPARATOR . 'say' . DIRECTORY_SEPARATOR . 'about' . DIRECTORY_SEPARATOR . 'hello.txt'], $files);

    $this->assertStringEqualsFile($tmpDir . '/some/other/folders/with/nothing/to/say/about/hello.txt', 'Sample String Data');
}

This seems to work fine on my machine and I can also open it using the default Linux unarchiver in the UI.

I therefore think that this is an issue with the unarchiver you're using. Can you try to use a different tool on the same file and see if that works?

— Reply to this email directly, view it on GitHub https://github.com/maennchen/ZipStream-PHP/issues/296#issuecomment-1903602373, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANVUI5K4KSFKYF53GQD5WSLYPYXWJAVCNFSM6AAAAABCEI7FI6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMBTGYYDEMZXGM . You are receiving this because you were mentioned.Message ID: @.***>

maennchen commented 8 months ago

Please test if you can open the file with other tools yourself. If it also does not work, it would be good if you could upload the file here.

I generally don't do free face to face support. If you would like to book a consulting session, you can have a look at my sponsors profile and book a session with me.

NicolasCARPi commented 8 months ago

I can also confirm that the issue is unreproducible, and it is likely that the unarchiver software is the culprit here.

As it turns out, much better software than WinRAR exist to deal with archives, such as the free and open source 7-zip.

d1ca commented 8 months ago

this is my entire code for this problem, it's a laravel app:

THE CONTROLLER

  /**
     * Downloads all files of a collection - original (or web for photos)
     * @throws OverflowException
     */
    public function download(BackBlazeService $service, string $reference, string $type = 'original'): int
    {
        abort_if(!in_array($type, ['original', 'web']), 404);
        $collection = Collection::with('descendants')->whereReference($reference)->firstOrFail();
        $allFiles= [];
        CollectionService::getFilesFromCollection($collection, $allFiles);
        $zip = CollectionService::createZip($collection->name);

        foreach ($allFiles as $data) {
            $file = $data['file'];
            $name = $data['name'];
            if ($file['backblaze_id']) {
                if ($type === 'web' && $file['type'] === EnumFileType::IMAGE) {
                    if (Storage::disk('public')->exists('hd/'.$file['path'])) {
                        $zip->addFile($name, Storage::disk('public')->get('hd/'.$file['path']));
                    }
                } else {
                    $zip->addFile($name, $service->get($file['backblaze_id']));
                }
            }
        }

        return $zip->finish();
    }

COLLECTION SERVICE

    /**
     * @param  Collection  $collection
     * @param  array  $allFiles
     * @param  string  $prefix
     * @return void
     */
    public static function getFilesFromCollection(Collection $collection, array &$allFiles = [], string $prefix = ''): void
    {
        if ($collection->files()->count()) {
            $files = $collection->files()->visible()->select('type', 'path', 'name', 'extension', 'backblaze_id')->get();
            foreach ($files as $file) {
                $filename = $prefix.$file->name.'.'.$file->extension;
                $allFiles[] = [
                    'name' => $filename,
                    'file' => $file->toArray()
                ];
            }
        }
        $subcollections = $collection->children;
        foreach ($subcollections as $children) {
            self::getFilesFromCollection($children, $allFiles, $prefix.$children->name.'/',);
        }
    }

note:

d1ca commented 8 months ago

this is my entire code for this problem, it's a laravel app:

THE CONTROLLER

  /**
     * Downloads all files of a collection - original (or web for photos)
     * @throws OverflowException
     */
    public function download(BackBlazeService $service, string $reference, string $type = 'original'): int
    {
        abort_if(!in_array($type, ['original', 'web']), 404);
        $collection = Collection::with('descendants')->whereReference($reference)->firstOrFail();
        $allFiles= [];
        CollectionService::getFilesFromCollection($collection, $allFiles);
        $zip = CollectionService::createZip($collection->name);

        foreach ($allFiles as $data) {
            $file = $data['file'];
            $name = $data['name'];
            if ($file['backblaze_id']) {
                if ($type === 'web' && $file['type'] === EnumFileType::IMAGE) {
                    if (Storage::disk('public')->exists('hd/'.$file['path'])) {
                        $zip->addFile($name, Storage::disk('public')->get('hd/'.$file['path']));
                    }
                } else {
                    $zip->addFile($name, $service->get($file['backblaze_id']));
                }
            }
        }

        return $zip->finish();
    }

COLLECTION SERVICE

    /**
     * @param  Collection  $collection
     * @param  array  $allFiles
     * @param  string  $prefix
     * @return void
     */
    public static function getFilesFromCollection(Collection $collection, array &$allFiles = [], string $prefix = ''): void
    {
        if ($collection->files()->count()) {
            $files = $collection->files()->visible()->select('type', 'path', 'name', 'extension', 'backblaze_id')->get();
            foreach ($files as $file) {
                $filename = $prefix.$file->name.'.'.$file->extension;
                $allFiles[] = [
                    'name' => $filename,
                    'file' => $file->toArray()
                ];
            }
        }
        $subcollections = $collection->children;
        foreach ($subcollections as $children) {
            self::getFilesFromCollection($children, $allFiles, $prefix.$children->name.'/',);
        }
    }

note:

d1ca commented 8 months ago

note:

maennchen commented 8 months ago

I see no obvious error on a first glance. Please try to open the file with another unarchiving tool to see if it works. If not, please share a file here so that we can see what is wrong with the generated file.

d1ca commented 8 months ago

Cristiano Ronaldo (2).zip This is a zip arhive with issue i've tried to open it also with WinZip Universal (windows 10) and i get same result

NicolasCARPi commented 8 months ago

If you use less to look at the zip file, you can see that debug code is added to the archive at the end:

2024-01-22-163747_917x295_scrot

I am not quite sure what exactly is the issue but now you have a full stack trace to help you debug ;) (might be best to not stream the zip but let the error happen so you can catch it with normal debug window)

d1ca commented 8 months ago

thank you, i guess it's not a bug :)