Uploading multiple files

Multiple files can be uploaded using different name for input.

It is also possible to upload multiple files simultaneously and have the information organized automatically in arrays for you. To do so, you need to use the same array submission syntax in the HTML form as you do with multiple selects and checkboxes:

Example #1 Uploading multiple files

<form action="file-upload.php" method="post" enctype="multipart/form-data">
  Send these files:<br />
  <input name="userfile[]" type="file" /><br />
  <input name="userfile[]" type="file" /><br />
  <input type="submit" value="Send files" />
</form>

When the above form is submitted, the arrays $_FILES['userfile'], $_FILES['userfile']['name'], and $_FILES['userfile']['size'] will be initialized.

For instance, assume that the filenames /home/test/review.html and /home/test/xwp.out are submitted. In this case, $_FILES['userfile']['name'][0] would contain the value review.html, and $_FILES['userfile']['name'][1] would contain the value xwp.out. Similarly, $_FILES['userfile']['size'][0] would contain review.html's file size, and so forth.

$_FILES['userfile']['name'][0], $_FILES['userfile']['tmp_name'][0], $_FILES['userfile']['size'][0], and $_FILES['userfile']['type'][0] are also set.

Warning

The max_file_uploads configuration setting acts as a limit on the number of files that can be uploaded in one request. You will need to ensure that your form does not try to upload more files in one request than this limit.

Example #2 Uploading an entire directory

In HTML file upload fields, it is possible to upload an entire directory with the webkitdirectory attribute. This feature is supported in most modern browsers.

With the full_path information, it is possible to store the relative paths, or reconstruct the same directory in the server.

<form action="file-upload.php" method="post" enctype="multipart/form-data">
  Send this directory:<br />
  <input name="userfile[]" type="file" webkitdirectory multiple />
  <input type="submit" value="Send files" />
</form>
Warning

The webkitdirectory attribute is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.

PHP only parses the relative path information submitted by the browser/user-agent, and passes that information to the $_FILES array. There is no guarantee that the values in the full_path array contains a real directory structure, and the PHP application must not trust this information.

add a note

User Contributed Notes 19 notes

up
354
phpuser at gmail dot com
19 years ago
When uploading multiple files, the $_FILES variable is created in the form:

Array
(
[name] => Array
(
[0] => foo.txt
[1] => bar.txt
)

[type] => Array
(
[0] => text/plain
[1] => text/plain
)

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

[error] => Array
(
[0] => 0
[1] => 0
)

[size] => Array
(
[0] => 123
[1] => 456
)
)

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.

<?php

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];
}
}

return
$file_ary;
}

?>

Now I can do the following:

<?php

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'];
}
}

?>
up
17
i.g.e.o@ya (dot) ru
4 years ago
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];

return
$file_ary;
}
?>
up
2
steam dot bakyt2 at gmail dot com
8 months ago
Just combine temporary path with the filename which will result an array like:

array(2) {
["/tmp/phpAYCvcc"]=> string(10) "file1.jpg"
["/tmp/phpCDg79o"]=> string(10) "file2.jpg"
}

The code:

$files = array_combine(
$_FILES['receipt']['tmp_name'],
$_FILES['receipt']['name']
);

foreach ($files as $key => $value) {
// save your files locally
}
up
51
wizzard351 at yahoo dot com
10 years ago
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.
up
12
Corey Ballou
14 years ago
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:

<?php
/**
* Fixes the odd indexing of multiple file uploads from the format:
*
* $_FILES['field']['key']['index']
*
* To the more standard and appropriate:
*
* $_FILES['field']['index']['key']
*
* @param array $files
* @author Corey Ballou
* @link http://www.jqueryin.com
*/
function fixFilesArray(&$files)
{
$names = array( 'name' => 1, 'type' => 1, 'tmp_name' => 1, 'error' => 1, 'size' => 1);

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]);
}
}
}
?>
up
15
timspeelman at live dot nl
12 years ago
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;
}
?>
up
5
lookphp at gmail dot com
8 years ago
This is a very simple example:

Part I : HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="upload.php" method="post" multipart="" enctype="multipart/form-data">
<input type="file" name="img[]" multiple>
<input type="submit">
</form>
</body>
</html>

Part II : PHP

<?php
echo '<pre>';
$img = $_FILES['img'];

if(!empty(
$img))
{
$img_desc = reArrayFiles($img);
print_r($img_desc);

foreach(
$img_desc as $val)
{
$newname = date('YmdHis',time()).mt_rand().'.jpg';
move_uploaded_file($val['tmp_name'],'./uploads/'.$newname);
}
}

function
reArrayFiles($file)
{
$file_ary = array();
$file_count = count($file['name']);
$file_key = array_keys($file);

for(
$i=0;$i<$file_count;$i++)
{
foreach(
$file_key as $val)
{
$file_ary[$i][$val] = $file[$val][$i];
}
}
return
$file_ary;
}
up
0
sabryabdelmohsen at gmail dot com
3 years ago
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;
}
up
-5
jess at semlabs dot co dot uk
15 years ago
If you try and upload files with multi-dimensional names like this:

<input type="file" name="submission[screenshot]" />
<input type="file" name="other[dem][][img][]" />

You will get an unexpected format like this:

<?php
array(
'submission' => array
(
'name' => array( 'screenshot' => 'monster_wallpaper.jpg' ),
'type' => array( 'screenshot' => 'image/jpeg' ),
'tmp_name' => array( 'screenshot' => '/tmp/php48lX2Y' ),
'error' => array( 'screenshot' => 0 ),
'size' => array( 'screenshot' => 223262 ),
),
....
?>

You can use the following function to re-format the array recursively in the usual format:

<?php
function format_files_array( $files, $name = null, &$new = false, $path = false ){
$names = array( 'name' => 'name', 'type' => 'type', 'tmp_name' => 'tmp_name', 'error' => 'error', 'size' => 'size' );

foreach(
$files as $key => &$part )
{
$key = ( string ) $key;
if(
in_array( $key, $names ) )
$name = $key;
if( !
in_array( $key, $names ) )
$path[] = $key;
if(
is_array( $part ) )
$part = format_files_array( $part, $name, $new, $path );
elseif( !
is_array( $part ) )
{
$current =& $new;
foreach(
$path as $p )
$current =& $current[$p];
$current[$name] = $part;
unset(
$path );
$name = null;
}
}

return
$new;
}
?>
up
-6
swayalex at gmail dot com
8 years ago
Recursive solution for complex situations (supports any nested arrays including indexed arrays)

function getFixedFilesArray() {
$walker = function ($arr, $fileInfokey, callable $walker) {
$ret = array();
foreach ($arr as $k => $v) {
if (is_array($v)) {
$ret[$k] = $walker($v, $fileInfokey, $walker);
} else {
$ret[$k][$fileInfokey] = $v;
}
}
return $ret;
};

$files = array();
foreach ($_FILES as $name => $values) {
// init for array_merge
if (!isset($files[$name])) {
$files[$name] = array();
}
if (!is_array($values['error'])) {
// normal syntax
$files[$name] = $values;
} else {
// html array feature
foreach ($values as $fileInfoKey => $subArray) {
$files[$name] = array_replace_recursive($files[$name], $walker($subArray, $fileInfoKey, $walker));
}
}
}

return $files;
}
up
-5
Eric
8 years ago
This is just a modification of the code which is the top note by "phpuser" here. His/her version requires that the $file_post array passed in to the function was created by a form submitted with the multiple attribute set. With multiple set in the html input tag, $_FILES["fileInputName"]["name"] is an array no matter if only one file is sent or multiple. But when <input type="file"> is used without the multiple attribute then $_FILES["fileInputName"]["name"] is not an array, it contains the the string with the filename. To use this neat function with or without multiple set and to get back an array which you can "foreach" over in either case, use this modification:

function reArrayFiles(&$file_post)
{
$file_ary = array();
$multiple = is_array($file_post['name']);

$file_count = $multiple ? count($file_post['name']) : 1;
$file_keys = array_keys($file_post);

for ($i=0; $i<$file_count; $i++)
{
foreach ($file_keys as $key)
{
$file_ary[$i][$key] = $multiple ? $file_post[$key][$i] : $file_post[$key];
}
}

return $file_ary;
}
up
-8
hotmail.com[at]notdefix
16 years ago
With multiple file uploads

post_max_size: the total amount of data posted by the client (all files, and all other form field)

upload_max_filesize: the maximum size of 1 single file. (just like <input type="hidden" name="MAX_FILE_SIZE" value="..."/>)

so, with the directives:
post_max_size 25M
upload_max_filesize 2M

you can send 12 files of up to 2 MB and use up to 1 MB for your additional form-values.

As long as you read only a single copy of 1 file into memory, the memory_limit directive can be held reasonable small as well.
up
-12
contato at dgmike dot com dot br
12 years ago
I prefer something like this!

<?php
public function arrayImages ( &$file_post )
{
if( empty(
$file_post ) ) {
return
$file_post;
}
if(
'array'!==gettype($file_post['name']) ) {
return
$file_post;
}
$keys = array_keys($file_post['name']);
$file_array = array();
foreach (
$keys as $key) {
foreach (
$file_post as $res=>$item) {
$file_array[$key][$res] = $item[$key];
}
}
return
$file_array;
}
?>
up
-10
jefrey no spam please at jefrey dot ml
7 years ago
Once I had to do a maintenance in a huge ERP that had several multiple upload inputs inside an array. Just like this:

<form method="post" enctype="multipart/form-data">
<input type="file" multiple name="upload[avatar]" />
<input type="file" multiple name="upload[attachment]" />
<input type="file" multiple name="upload2[avatar]" />
<input type="file" multiple name="upload2[attachment]" />
<input type="submit" />
</form>

The $_FILES array is created like this:

Array
(
[upload] => Array
(
[name] => Array
(
[avatar] => teste.c
[attachment] => teste
)

[type] => Array
(
[avatar] => text/x-csrc
[attachment] => application/octet-stream
)

[tmp_name] => Array
(
[avatar] => /opt/lampp/temp/phpuf3KNj
[attachment] => /opt/lampp/temp/php0yPZap
)

[error] => Array
(
[avatar] => 0
[attachment] => 0
)

[size] => Array
(
[avatar] => 1960
[attachment] => 8661
)

)

[upload2] => Array
(
[name] => Array
(
[avatar] => jefrey.html
[attachment] => notas.txt
)

[type] => Array
(
[avatar] => text/html
[attachment] => text/plain
)

[tmp_name] => Array
(
[avatar] => /opt/lampp/temp/php87nfyu
[attachment] => /opt/lampp/temp/phpUBlvVz
)

[error] => Array
(
[avatar] => 0
[attachment] => 0
)

[size] => Array
(
[avatar] => 583
[attachment] => 191
)

)

)

I've managed to re-arrange this array like this:

Array
(
[upload] => Array
(
[avatar] => Array
(
[name] => teste.c
[type] => text/x-csrc
[tmp_name] => /opt/lampp/temp/phpuf3KNj
[error] => 0
[size] => 1960
)

[attachment] => Array
(
[name] => teste
[type] => application/octet-stream
[tmp_name] => /opt/lampp/temp/php0yPZap
[error] => 0
[size] => 8661
)

)

[upload2] => Array
(
[avatar] => Array
(
[name] => jefrey.html
[type] => text/html
[tmp_name] => /opt/lampp/temp/php87nfyu
[error] => 0
[size] => 583
)

[attachment] => Array
(
[name] => notas.txt
[type] => text/plain
[tmp_name] => /opt/lampp/temp/phpUBlvVz
[error] => 0
[size] => 191
)

)

)

Here's my snippet:
<?php
function reArrayFilesMultiple(&$files) {
$uploads = array();
foreach(
$_FILES as $key0=>$FILES) {
foreach(
$FILES as $key=>$value) {
foreach(
$value as $key2=>$value2) {
$uploads[$key0][$key2][$key] = $value2;
}
}
}
$files = $uploads;
return
$uploads; // prevent misuse issue
}
?>
up
-32
javad dot geek at gmail dot com
10 years ago
$countarray = count($_FILES['uploadfile']['name']);
$newarray = array();
for($i=0;$i<$countarray;$i++){
$newarray[$i]['name']=$_FILES['uploadfile']['name'][$i];
$newarray[$i]['type']=$_FILES['uploadfile']['type'][$i];
$newarray[$i]['tmp_name']=$_FILES['uploadfile']['tmp_name'][$i];
$newarray[$i]['error']=$_FILES['uploadfile']['error'][$i];
$newarray[$i]['size']=$_FILES['uploadfile']['size'][$i];
}
up
-4
simbiat at outlook dot com
3 years ago
While code from phpuser.at.gmail.dot.com is good and i.g.e.o@ya.(dot).ru is even better, there is no end to perfection. We can live without temporary variables
<?php
foreach ($_FILES as $field=>$files) {
#Check if multiple files were uploaded to a field and process the values accordingly
if (is_array($files['name'])) {
foreach (
$files['name'] as $key=>$file) {
$_FILES[$field][$key]['name'] = $file;
$_FILES[$field][$key]['type'] = $files['type'][$key];
$_FILES[$field][$key]['size'] = $files['size'][$key];
$_FILES[$field][$key]['tmp_name'] = $files['tmp_name'][$key];
$_FILES[$field][$key]['error'] = $files['error'][$key];
}
} else {
$_FILES[$field][0]['name'] = $files['name'];
$_FILES[$field][0]['type'] = $files['type'];
$_FILES[$field][0]['size'] = $files['size'];
$_FILES[$field][0]['tmp_name'] = $files['tmp_name'];
$_FILES[$field][0]['error'] = $files['error'];
}
unset(
$_FILES[$field]['name'], $_FILES[$field]['type'], $_FILES[$field]['size'], $_FILES[$field]['tmp_name'], $_FILES[$field]['error']);
}
?>
up
-36
ohcnim at hotmail dot com
10 years ago
by simply naming differently each file input you'll get easily accesible arrays from $_FILES, in the form $_FILES['input_name']['file_attribute']. For example:

$_FILES['input_name1']['name']...['input_name1']['size']
$_FILES['input_name2']['name']...['input_name2']['size']
$_FILES['input_nameX']['name']...['input_nameX']['size']
up
-34
Pako
8 years ago
If you want to upload multiple file at once, remember "multiple" attribute:

<input type="file" multiple="multiple" name="file[]" enctype="multipart/form-data"/>
up
-31
Roman
8 years ago
function reorganize($files) {
foreach ($files as $var => $params) {
foreach ($params as $name => $i) {
foreach ($i as $num => $val) {
$images[$var][$name] = $val;
$arr[$num] = $images;
}
}
}
return $arr;
}

Array (
[0] => Array (
[image] => Array (
[name] => white-rabbit-med-crop.jpg
[type] => image/jpeg
[tmp_name] => E:\xampp\tmp\phpC008.tmp
[error] => 0
[size] => 343326 )
)
[1] => Array (
[image] => Array (
[name] => white-rabbit-med-crop.jpg
[type] => image/jpeg
[tmp_name] => E:\xampp\tmp\phpC008.tmp
[error] => 0
[size] => 1429802 )
)
)
To Top