Использование ZipStream в Symfony: потоковая загрузка zip не будет распаковываться с помощью Archive Utility на Mac OSX
У меня есть приложение Symfony 2.8, и, нажимая кнопку "скачать", я использую ключи нескольких больших файлов (изображения, видео) на s3 для потоковой передачи в браузер в виде zip-файла с использованием ZipStream ( https://github.com/maennchen/ZipStream-PHP).
Потоковая передача файлов и загрузка в виде zip-файла (сохраненного, а не сжатого) успешно выполняется в браузере, и zip-файл попадает в раздел Downloads. Однако при попытке открыть zip-архив в Mac OSX El Capitan с помощью служебной программы архивирования (встроенного программного обеспечения для архивирования в OSX) происходит ошибка. Ошибка:
Невозможно развернуть "test.zip" в "Загрузки". (Ошибка 2 - Нет такого файла или каталога.)
Я видел более старые, идентичные проблемы с SO и пытался их исправить, в частности, этот пост: /questions/34210076/dinamicheski-sozdavaemyie-zip-fajlyi-zipstream-v-php-ne-otkryivayutsya-v-osx/34210102#34210102 и следил за связанными с ними проблемами и связями с ZipStream, а также последующими исправлениями в Guzzle и т.д.,
Проблема в том, что вышеперечисленные исправления были еще в 2011 году, и в это время дела пошли еще дальше. Поэтому, применяя те же исправления, я не получаю рабочего результата.
Конкретные исправления, которые я попробовал: 1. Установка "версии для извлечения" в 0x000A, как предложено. Также как "20", как рекомендуется в другом посте. Я установил то же самое для "версия сделана". 2. Я попытался заставить метод сжатия "сдуть", а не "сохранить", чтобы посмотреть, получился ли у меня рабочий результат. Сохраненный результат - все, что мне нужно и подходит для почтового индекса, используемого в качестве файла-контейнера для изображений и видео.
Я могу извлечь zip с помощью стороннего архивного приложения под названием The Unarchiver. Тем не менее, пользователи не будут знать и не смогут установить альтернативное архивное приложение, подходящее для моего веб-приложения. Это не эффективное решение.
Кто-нибудь имеет знания или опыт решения этой проблемы и может помочь мне с ее решением?
NB. Потоковый почтовый индекс в браузер является необходимым решением. Загрузка ресурсов с сервера s3 на сервер для создания zip-файла и последующей передачи полученного zip-файла в браузер не является решением, учитывая количество времени и накладных расходов, связанных с таким подходом.
Добавлена информация, если требуется:
Код и настройка: - Файлы хранятся на s3. - Веб-приложение Symfony 2.8 на PHP7.0 работает на ec2 с Ubuntu. - Используя aws-sdk-php, создайте s3client с действительными учетными данными, и я регистрирую StreamWrapper (s3client->registerStreamWrapper()) на s3client. Это для извлечения файлов из s3 через fopen для потоковой передачи в библиотеку ZipStream:
$this->s3Client = $s3Client;
$this->s3Client->registerStreamWrapper();
// Initialize the ZipStream object and pass in the file name which
// will be what is sent in the content-disposition header.
// This is the name of the file which will be sent to the client.
// Define suitable options for ZipStream Archive.
$opt = array(
'comment' => 'test zip file.',
'content_type' => 'application/octet-stream'
);
$zip = new ZipStream\ZipStream($filename, $opt);
$keys = array(
"zipsimpletestfolder/file1.txt"
);
foreach ($keys 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);
$bucket = 'mg-test';
$s3path = "s3://" . $bucket . "/" . $key;
if ($streamRead = fopen($s3path, 'r')) {
$zip->addFileFromStream($fileName, $streamRead);
} else {
die('Could not open stream for reading');
}
}
$zip->finish();
Результаты вывода Zip:
Извлечение на Mac с помощью Archive Utility завершается с ошибкой 2
Извлечение на Mac с Unarchiver работает.
Добыча на окнах с 7-почтовыми работами.
Извлечение на Windows с WinRar не удается - говорит, что ZIP поврежден.
Заголовки ответа:
Редактировать Я открыт для использования другого метода потоковой передачи файлов в браузер в виде почтового индекса, который можно открыть на Mac, Windows, Linux без необходимости использовать ZipStream, если это предлагается. Только чтобы не создавать сторону сервера zip-файлов для последующей потоковой передачи.
1 ответ
Проблема с загруженным zip-файлом заключается в том, что он содержал html из ответа контроллера Symfony, который вызывал ZipStream->addFileFromStream(). В основном, когда ZipStream осуществлял потоковую передачу данных для создания zip-загрузки в клиентском браузере, также отправлялся ответ на действие контроллера, и, скорее всего, оба были перепутаны в клиентском браузере. Открыв zip-файл в шестнадцатеричном редакторе и увидев там html, очевидно, это была проблема.
Чтобы это работало в Symfony 2.8 с ZipStream, я просто использовал Symfony StreamedResponse в действии контроллера и использовал ZipStream в функции streamedResponse. Для потоковой передачи файлов S3 я просто передал массив s3keys и s3client в эту функцию. Так:
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;
}
Решаемые. Может быть, это поможет кому-то еще использовать ZipStream в Symfony.