Динамически создаваемые zip-файлы ZipStream в PHP не открываются в OSX - PullRequest
10 голосов
/ 07 апреля 2011

У меня есть PHP-сайт с большим количеством медиа-файлов, и пользователи должны иметь возможность загружать несколько файлов одновременно в формате .zip. Я пытаюсь использовать ZipStream для обслуживания архивов на лету со сжатием "store", поэтому мне не нужно создавать архив на сервере, так как некоторые файлы огромны, и это непозволительно медленно сжимать их все.

Это прекрасно работает, и полученные файлы могут быть открыты всеми программами zip, которые я пробовал, без ошибок, за исключением стандартной программы разархивирования OS X, Archive Utility. Вы дважды щелкаете по файлу .zip, и Archive Utility решает, что он не выглядит как настоящий zip, и вместо этого сжимает в файл .cpgz.

Использование unzip или ditto в терминале OS X или StuffIt Expander без проблем распаковывает файл, но мне нужна программа по умолчанию (Archive Utility), чтобы работать ради наших пользователей.

Какие вещи (флаги и т. Д.) В других приемлемых zip-файлах могут заставить Archive Utility думать, что файл не является действительным zip-файлом?

Я прочитал этот вопрос , который, кажется, описывает аналогичную проблему, но у меня не установлены биты общего поля битов общего назначения, так что это не проблема третьего бита, и я уверен, У меня есть действующие crc-32, потому что, когда у меня их нет, WinRAR выбрасывает.

Я рад опубликовать некоторый код или ссылку на «плохой» zip-файл, если это поможет, но я в основном просто использую ZipStream, переводя его в «режим больших файлов» и используя «store» в качестве метод сжатия.

Редактировать - я тоже попробовал алгоритм сжатия "deflate" и получил те же результаты, поэтому я не думаю, что это "магазин". Стоит также отметить, что я извлекаю файлы один раз с сервера хранения и отправляю их по мере их поступления, поэтому решение, требующее загрузки всех файлов перед отправкой чего-либо, не будет жизнеспособным (крайне Пример: 5 ГБ + 20 МБ файлов. Пользователь не может дождаться, когда все 5 ГБ будут переданы на почтовый сервер, прежде чем начнется их загрузка, или они подумают, что он поврежден)

Вот 140-байтовый сжатый тестовый zip-файл, сохраняющий это поведение: http://teknocowboys.com/test.zip

Ответы [ 6 ]

9 голосов
/ 07 апреля 2011

Проблема была в поле «версия, необходимая для извлечения», которое я обнаружил, выполнив шестнадцатеричное сравнение с файлом, созданным ZipStream, и файлом, созданным Info-zip, и проанализировал различия, пытаясь их устранить.

ZipStream по умолчанию устанавливает его в 0x0603. Info-zip устанавливает его на 0x000A. Zip-файлы с прежним значением не открываются в Archive Utility. Возможно, он не поддерживает функции этой версии?

При принудительной установке «версии, необходимой для извлечения», равной 0x000A, сгенерированные файлы были открыты также в служебной программе архивирования, как и везде.

Редактировать. Другая причина этой проблемы - если zip-файл был загружен с использованием Safari (версия пользовательского агента> = 537) и вы указали заниженный размер файла при отправке заголовка Content-Length.

Решение, которое мы используем, состоит в том, чтобы обнаружить Safari> = 537 на стороне сервера, и если это то, что вы используете, мы определим разницу между размером Content-Length и фактическим размером (как вы это сделаете, зависит от вашего конкретного приложения) и после вызова $ zipStream-> finish () мы выводим chr (0), чтобы получить правильную длину. Полученный файл технически искажен, и любой комментарий, помещенный в zip, не будет отображаться, но все программы zip смогут открыть его и извлечь файлы.

IE требует того же хака, если вы неверно сообщаете свой Content-Length, но вместо загрузки файла, который не работает, он просто не заканчивает загрузку и выдает «загрузка прервана».

3 голосов
/ 13 ноября 2012

используйте ob_clean (); и flush ();

Пример:

    $file =  __UPLOAD_PATH . $projectname . '/' . $fileName;

    $zipname = "watherver.zip"
    $zip = new ZipArchive(); 
    $zip_full_path_name = __UPLOAD_PATH . $projectname . '/' . $zipname;
    $zip->open($zip_full_path_name, ZIPARCHIVE::CREATE);
    $zip->addFile($file); // Adding one file for testing
    $zip->close();

    if(file_exists($zip_full_path_name)){
        header('Content-type: application/zip');
        header('Content-Disposition: attachment; filename="'.$zipname.'"');
        ob_clean();
        flush();
        readfile($zip_full_path_name);
        unlink($zip_full_path_name);
    }
2 голосов
/ 11 августа 2014

У меня была именно эта проблема, но с другой причиной.

В моем случае сгенерированный php zip открывался из командной строки, но не через finder в OSX.

Iдопустил ошибку, допуская некоторое HTML-содержимое в выходной буфер до создания zip-файла и отправки его обратно в качестве ответа.

<some html></....>
<?php

// Output a zip file...

Программа разархивирования из командной строки, очевидно, терпимо относилась к этому, но разархивировать Macфункции не было.

1 голос
/ 23 июня 2017

Для тех, кто использует ZipStream в Symfony, вот ваше решение: https://stackoverflow.com/a/44706446/136151

use Symfony\Component\HttpFoundation\StreamedResponse;
use Aws\S3\S3Client;    
use ZipStream;

//...

/**
 * @Route("/zipstream", name="zipstream")
 */
public function zipStreamAction()
{
    //test file on s3
    $s3keys = array(
      "ziptestfolder/file1.txt"
    );

    $s3Client = $this->get('app.amazon.s3'); //s3client service
    $s3Client->registerStreamWrapper(); //required

    $response = new StreamedResponse(function() use($s3keys, $s3Client) 
    {

        // Define suitable options for ZipStream Archive.
        $opt = array(
                'comment' => 'test zip file.',
                'content_type' => 'application/octet-stream'
              );
        //initialise zipstream with output zip filename and options.
        $zip = new ZipStream\ZipStream('test.zip', $opt);

        //loop keys useful for multiple files
        foreach ($s3keys as $key) {
            // Get the file name in S3 key so we can save it to the zip 
            //file using the same name.
            $fileName = basename($key);

            //concatenate s3path.
            $bucket = 'bucketname';
            $s3path = "s3://" . $bucket . "/" . $key;        

            //addFileFromStream
            if ($streamRead = fopen($s3path, 'r')) {
              $zip->addFileFromStream($fileName, $streamRead);        
            } else {
              die('Could not open stream for reading');
            }
        }

        $zip->finish();

    });

    return $response;
}

Если ваш ответ на действие контроллера не является StreamedResponse, вы, скорее всего, получите поврежденный zip-файл, содержащий html, как я обнаружилвне.

1 голос
/ 21 мая 2011

Инструмент командной строки InfoZip, который я использую как в Windows, так и в Linux, использует версию 20 для поля «версия, необходимая для извлечения» zip.Это необходимо и для PHP, так как по умолчанию используется алгоритм Deflate.Таким образом, поле «версия, необходимая для извлечения» должно быть действительно 0x0014.Если вы измените код «(6 << 8) +3» в указанном классе ZipStream на «20», вы должны получить действительный Zip-файл для разных платформ. </p>

Автор в основном говорит вам, чтоzip-файл был создан в OS / 2 с использованием файловой системы HPFS, и необходимая Zip-версия предшествовала InfoZip 1.0.Не многие реализации знают, что с этим делать дальше;)

1 голос
/ 07 апреля 2011

Понятия не имею.Если внешний класс ZipString не работает, попробуйте другой вариант.Расширение PHP ZipArchive вам не поможет, поскольку оно не поддерживает потоковую передачу, а только когда-либо записывает в файлы.

Но вы можете попробовать стандартную утилиту Info-zip.Он может быть вызван изнутри PHP следующим образом:

#header("Content-Type: archive/zip");
passthru("zip -0 -q -r - *.*");

Это приведет к тому, что несжатый zip-файл будет напрямую отправлен обратно клиенту.

Если это не поможет, то MacOSZip-интерфейс, вероятно, не любит несжатый материал.Снимите флаг -0, затем.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...