Загрузка сразу нескольких файлов

Обновлено: 07.07.2024

Если нужно дать пользователю возможность загрузки нескольких файлов, традиционное решение на данный момент — использовать для этой цели Flash (реже — Java applet или ActiveX). В случае, если соответствующий плагин недоступен, пользователю, как правило, показывают стандартный HTML-элемент для загрузки файла.

Последнюю ситуацию можно улучшить, если использовать встроенную в браузеры возможность множественной загрузки файлов. Из всех браузеров сейчас данную возможность не поддерживает только Internet Explorer (впрочем, мы ещё не видели девятую версию, может там что-то изменится), остальные браузеры — Opera 9 и выше (а так же версии 3.5—6.05), Firefox 3.6+, Chrome 3.0.191.0+ и Safari 4.0.1+ такую возможность предоставляют.

Достаточно написать что-то вроде

  1. < form enctype ="multipart/form-data" method ="post">
  2. < input type ="file" min ="1" max ="9999" name ="file[]" multiple ="true" />
  3. < input type ="submit" name ="submit" />
  4. </ form >

PHP оказался готов к такой конструкции (именно для него в параметре «name» стоят квадратные скобки), он просто разложит загружаемые файлы по элементам массива $_FILES, если только мы не используем «Оперу».

К сожалению, «Опера» (ещё с версии 3.5) отправляет, при использовании мультизагрузки, файлы в контейнере «multipart/mixed», который PHP не понимает.

В качестве парсера я использовал PECL-модуль mailparse (есть бинарник для Windows).

У меня в примере ожидается параметр «file», но это значение легко вынести в настройку. Код мне кажется достаточно простым, чтобы его не комментировать, но, если что-то не понятно, спросите, я добавлю комментарии.

  1. if ( isset ( $_POST [ 'file' ], $_POST [ 'file' ][ 0 ]))
  2. if ( $idx = strpos( $_POST [ 'file' ][ 0 ], "\n" ))
  3. $bound = substr( $_POST [ 'file' ][ 0 ], 2 , $idx - 2 );
  4. $body = "MIME-Version: 1.0\nContent-type: multipart/form-data; boundary=\n\n" .
  5. $_POST [ 'file' ][ 0 ];
  6. unset ( $_POST [ 'file' ][ 0 ]);
  7. $f = & $_FILES [ 'file' ];
  8. $f [ 'name' ] = $f [ 'type' ] = $f [ 'tmp_name' ] = $f [ 'error' ] = $f [ 'size' ];
  9. $msg = mailparse_msg_create();
  10. if (mailparse_msg_parse( $msg , $body ))
  11. $i = 0 ;
  12. foreach (mailparse_msg_get_structure( $msg ) as $st )
  13. $section = mailparse_msg_get_part( $msg , $st );
  14. $data = mailparse_msg_get_part_data( $section );
  15. if ( $data [ 'content-type' ] == 'multipart/form-data' )
  16. continue ;
  17. >
  18. ob_start();
  19. if (mailparse_msg_extract_part( $section , $body ))
  20. $tmp = tempnam(sys_get_temp_dir(), 'php' );
  21. file_put_contents( $tmp , ob_get_clean());
  22. $f [ 'name' ][ $i ] = $data [ 'disposition-filename' ];
  23. $f [ 'type' ][ $i ] = $data [ 'content-type' ];
  24. $f [ 'tmp_name' ][ $i ] = $tmp ;
  25. $f [ 'error' ][ $i ] = 0 ;
  26. $f [ 'size' ][ $i ] = filesize( $tmp );
  27. $i ++;
  28. > else
  29. ob_end_clean();
  30. >
  31. >
  32. >
  33. unset ( $f );
  34. mailparse_msg_free( $msg );
  35. >
  36. >

Я не совсем уверен насчёт публикации этой статьи в блог «PHP», возможно «HTML» подошёл бы больше, с другой стороны, здесь рассматривается способ использования множественной загрузки вместе в PHP.

When uploading multiple files, the $_FILES variable is created in the form:

[tmp_name] => Array
(
[0] => /tmp/phpYzdqkD
[1] => /tmp/phpeEwEWG
)

I found it made for a little cleaner code if I had the uploaded files array in the form

Array
(
[0] => Array
(
[name] => foo.txt
[type] => text/plain
[tmp_name] => /tmp/phpYzdqkD
[error] => 0
[size] => 123
)

[1] => Array
(
[name] => bar.txt
[type] => text/plain
[tmp_name] => /tmp/phpeEwEWG
[error] => 0
[size] => 456
)
)

I wrote a quick function that would convert the $_FILES array to the cleaner (IMHO) array.

function reArrayFiles (& $file_post )

$file_ary = array();
$file_count = count ( $file_post [ 'name' ]);
$file_keys = array_keys ( $file_post );

for ( $i = 0 ; $i < $file_count ; $i ++) foreach ( $file_keys as $key ) $file_ary [ $i ][ $key ] = $file_post [ $key ][ $i ];
>
>

?>

Now I can do the following:

if ( $_FILES [ 'upload' ]) $file_ary = reArrayFiles ( $_FILES [ 'ufile' ]);

foreach ( $file_ary as $file ) print 'File Name: ' . $file [ 'name' ];
print 'File Type: ' . $file [ 'type' ];
print 'File Size: ' . $file [ 'size' ];
>
>

A bit update to 14 year ago note from "phpuser at gmail dot com".
That update converts to a really more friendly array form incoming _POST info for uploaded files.
And that variants works identical for non-multiple uploads and multiple uploads:
<?php
//Функция переформатирует массив поданных POST'ом файлов
function reArrayFiles (& $file_post ) $isMulti = is_array ( $file_post [ 'name' ]);
$file_count = $isMulti ? count ( $file_post [ 'name' ]): 1 ;
$file_keys = array_keys ( $file_post );

$file_ary = []; //Итоговый массив
for( $i = 0 ; $i < $file_count ; $i ++)
foreach( $file_keys as $key )
if( $isMulti )
$file_ary [ $i ][ $key ] = $file_post [ $key ][ $i ];
else
$file_ary [ $i ][ $key ] = $file_post [ $key ];

This is also needed for <input type=file multiple> elements.

So, if you have an input element like this:
<input type="file" multiple="multiple" name="foobar" />
This should be written as
<input type="file" multiple="multiple" name="foobar[]" />
else you'll only be able to get one of the files.

function reArrayImages($file_post) <
$file_ary = [];
$file_keys = array_keys($file_post);
foreach ($file_post as $key => $value) <
foreach ($value as $key2 => $value2) <
$file_ary[$key2][$key] = $value2;
>
>
return $file_ary;
>

The cleanest way to rearrange the $_FILES

<?php
function rearrange ( $arr ) foreach( $arr as $key => $all ) foreach( $all as $i => $val ) $new [ $i ][ $key ] = $val ;
>
>
return $new ;
>
?>

Here is a function to fix the indices of a multi-dimensional for easier parsing when dealing with file uploads. It takes a single $_FILES field array as a parameter and separates each individual uploaded file by numeric key. This allows for iterating like:

<?php
fixFilesArray ( $_FILES [ 'array_of_files' ]);
foreach ( $_FILES [ 'array_of_files' ] as $position => $file ) // should output array with indices name, type, tmp_name, error, size
var_dump ( $file );
>
?>

Here's the code:

foreach ( $files as $key => $part ) // only deal with valid keys and multiple files
$key = (string) $key ;
if (isset( $names [ $key ]) && is_array ( $part )) foreach ( $part as $position => $value ) $files [ $position ][ $key ] = $value ;
>
// remove old key reference
unset( $files [ $key ]);
>
>
>
?>


Вы пытаетесь загрузить несколько файлов одновременно? Вот как реализовать загрузку нескольких файлов с использованием HTML и PHP.

В этой статье я собираюсь показать, как использовать один HTML-файл для загрузки нескольких файлов. В дополнение к этому я продемонстрирую использование нескольких файловых входов с дополнительными полями ввода.

Реализация загрузки нескольких файлов

Во-первых, вам нужно создать HTML- форму с атрибутом enctype = ‘multiple / form-data’ . Фактически, атрибут enctype указывает, как данные формы должны быть закодированы при отправке их на сервер. Когда вы используете формы, которые имеют элемент управления загрузкой файлов, вам нужно указать enctype как множественные / form-data .

Если вы используете ввод одного файла, вам нужно включить элемент file для выбора нескольких файлов. Чтобы сделать это, вам нужно назвать входной файл в виде массива, например. имя = “файлы []” . Кроме того, элемент «Файл ввода» должен иметь несколько = «несколько» или просто несколько.

Форма HTML будет выглядеть следующим образом:

Когда пользователь отправляет форму после выбора файлов, мы можем обработать форму с помощью простых фрагментов PHP следующим образом:

Проверка типа файла и размера файла

Вы можете ограничить тип файла, проверив расширение загруженного файла с помощью набора разрешенных расширений. Следующий код проверяет правильность файла изображения.

Чтобы проверить размер файла, вы можете использовать $ _FILES [‘image’] [‘size’] следующим образом:

Кроме того, вы также можете переименовать имя файла перед загрузкой. Вот как заменить пробелы в имени файла на подчеркивание и добавить метку времени к имени файла.

Загрузка нескольких файлов с дополнительной информацией

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

На приведенном выше снимке экрана каждый входной файл имеет соответствующий заголовок. Вот пример HTML.

Как видите, есть несколько элементов управления вводом текста и файлов. Добавляя ‘[]’ к вашим именам входных элементов, входные элементы будут передаваться как массивы.

Когда вы отправляете вышеуказанную форму, $ _POST [‘title’] будет массивом. Сопоставляя индекс массива $ _POST [‘title’] с $ _FILES [‘fileUpload’] [‘name’], вы можете получить соответствующий заголовок, пару имен файлов. Например, $ _POST [‘title’] [0] – это заголовок файла $ _FILES [‘fileUpload’] [‘name’] [0] и так далее.

Таким образом, вы можете реализовать несколько загрузок файлов с использованием HTML и PHP.

Возьмём по умолчанию Chrome v.88. Задача звучит так:



Само собой, можно решить такую задачу, просто сжав все нужные файлы в один ZIP-архив, а потом уже скачать его. Выходит, пользователь скачает единый файл, который потом самостоятельно разархивирует. Например, можно использовать библиотеку jszip, которая позволяет скачивать набор файлов, сжав их.

Вот небольшой пример скачивания с предварительным сжатием из документации:


«А где тут нетривиальность?» — спросите вы. И будете правы. А если речь идёт об одновременном скачивании с сайта двух, трёх или десяти файлов? Например: есть лист в селекте, в котором можно выбрать определённое количество файлов для скачивания. Введём дополнительное условие: у пользователя нет установленного архиватора, поэтому вариант со сжатием в архив отбрасываем. Как решить такую задачу?

Сначала подготовим браузер. Chrome по умолчанию запрещает скачивать мультифайлы. Это сделано в целях безопасности. Поэтому эту функцию надо сначала разблокировать в настройках браузера:

  1. Заходим в настройки сайтов: chrome://settings/content.
  2. Переходим в доступ дополнительных прав (Additional permissions).
  3. Выбираем Automatic downloads.
  4. Добавляем необходимый сайт в категорию Allow.


Отлично, теперь ваш браузер стал менее безопасным позволяет скачивать на конкретном сайте сразу несколько файлов.

Первый подход рассмотрим на примере генерации файлов с помощью FileReader и API для чтения base64. Отмечу сразу, что у FileReader довольно обширное API, поэтому выбирайте то, что больше нравится: text, arrayBuffer или binaryString.

А ещё можно использовать createObjectURL — он позволяет хранить File-объекты или Blob-объекты.

Два варианта выше генерируют файлы на стороне клиента. Конечно, так будет не всегда, время от времени мы получаем файлы со стороны бэкенда по прямым ссылкам. Это можно реализовать с помощью скачивания по URL. Chrome требует, чтобы была задержка, поэтому особенностью этого метода и станет реализация искусственной задержки.

Читайте также: