Первоначально опубликовано на http://engineering.weebly.com/

Вы заметили, что на Dropbox.com вы можете выбрать папку и начать ее загрузку, пока она архивируется? Эта функция архивирования на лету удобна как для пользователя, так и для сервера - как пользователю вам не нужно ждать, пока файлы будут заархивированы на серверной стороне, прежде чем начнется загрузка, и это избавляет сервер от создания временный zip-файл с последующим его удалением.

В этом случае браузер не знает, когда закончится потоковый zip-файл или насколько велик будет весь zip-файл, поэтому пользователь увидит что-то вроде:

Во вкладке Network такой запрос выглядит так:

Request Headers (excerpt):
accept:text/plain, */*; q=0.01
accept-encoding:gzip, deflate
content-length:205
content-type:application/x-www-form-urlencoded; charset=UTF-8
 
Response Headers (excerpt):
content-disposition:attachment; filename=”batch_download.zip”
content-type:application/x-zip
content-transfer-encoding:binary
date:Fri, 16 Dec 2016 20:01:47 GMT
server:nginx
Status:200

Обратите внимание, что для параметра content-disposition установлено значение attachment. Это означает, что браузер должен интерпретировать ответ как загрузку файла, а не пытаться отобразить ответ как веб-страницу. Подробнее о том, как работает эта опция, читайте в этой статье о MDN.

Чтобы реализовать эту функцию с помощью PHP, мы обрабатываем архивирование и потоковую передачу на серверной части с помощью ZipStream. Предполагая, что ваши файлы находятся на сервере и могут быть загружены с помощью file_get_contents с указанием пути:

Под капотом, когда вы вызываете $ zip- ›finish (), ZipStream заботится об установке заголовков ответа (content-disposition, content-type и т. Д.) И проверяет, обрабатывает ли браузер этот ответ как загрузка файла. Наконец, он вызывает fwrite для отправки потока данных.

Если ваши файлы размещены в облачных сервисах, таких как AWS / S3, вам необходимо использовать соответствующий метод для загрузки содержимого файла и вызвать метод нижнего уровня ZipStream $ zip- ›addFile ($ file_name, $ file_content ). См. Следующий пример с AWS / S3:

$file_name = ‘my_awesome_picture.jpg’;
$file = $client->getObject([
 ‘Bucket’: ‘my_pictures_bucket’,
 ‘Key’: $file_name
]);
$file_content = (string) $file[‘Body’];
// similar to above, except the second parameter being file content
$zip->addFile($file_name, $file_content);

Во внешнем интерфейсе все, что нам нужно сделать, это отправить запрос с полезной нагрузкой идентификаторов выбранных пользователем файлов и / или папок. Это зависит от вашего приложения. Также стоит отметить, что этот запрос не может быть выполнен через Ajax из соображений безопасности - в этом случае все, что Ajax мог сделать, это получить заархивированный файл в виде двоичной строки, но не может инициировать загрузку файла. Приходится прибегать к HTML нижнего уровня ‹form›. Вставьте следующий вспомогательный ‹form› в любое место в DOM:

Обратите внимание, что хотя этот запрос включает получение заархивированного файла из серверной части, мы используем здесь метод POST просто потому, что полезная нагрузка запроса может стать довольно длинной, если пользователь выбрал загрузку файлов / папок. В этом случае метод GET может закончиться ошибкой HTTP 414.

Следующее, что нужно сделать, это загрузить данные в эту форму (вставив ‹input› s) и отправить их через Javascript:

var $form = $(‘#downloader-form’);
// Append CSRF token for POST method
$form.append($(‘<input>’).attr({
 name: ‘_token’,
 value: ‘<your_CSRF_token>’
}));
// User-selected files, assuming files are identified by UUIDs
$form.append($(‘<input>’).attr({
 name: ‘file_ids’,
 value: [‘df491ae4–9d00–4674-b565-e4e5943f55b4’, …]
}));
$form.submit();

Пара дополнительных вопросов, о которых стоит упомянуть:

(1) По завершении отправки формы браузер интерпретирует ответ вашей формы как документ, но один из заголовков ответа, content-type, указывает, что типом MIME является application / x-zip вместо допустимого типа MIME документа, такого как text / html. Так что вы, вероятно, увидите этот журнал в своей консоли:

Ресурс интерпретируется как документ, но передается с использованием типа MIME application / zip: «http://yoursite.com/batch-download.

Это неверное толкование кажется вполне разумным, но, похоже, нет никакого обходного пути для этого. ‹form› был предназначен в первую очередь для отправки формы и обновления страницы для отображения ответа сервера в виде документа.

(2) У вас может возникнуть соблазн прослушать событие complete отправки формы, чтобы, например, вы могли повторно включить кнопку Загрузить. Однако этого нельзя добиться. Подводя итог обсуждению этого вопроса здесь, когда вы публикуете форму, входные данные формы отправляются на сервер, и ваша страница должна обновляться.

Е Тянь

Разработчик программного обеспечения полного стека в Weebly