Как вы перебираете массив $_FILES?
Вот входы, которые я хочу пройти через
Main photo: <input type="file" name="image[]" />
Side photo 1: <input type="file" name="image[]" />
Side photo 2: <input type="file" name="image[]" />
Side photo 3: <input type="file" name="image[]" />
Пара странных вещей произошла, когда я ничего не загрузил, я использую count($_FILES['image'])
Я повторил эту функцию, и она возвращает значение 5. В этом массиве не должно быть элементов. Почему есть один дополнительный вход, если у меня есть только 4 файла для начала?
Теперь с самим циклом, я пытаюсь использовать цикл foreach, но он не работает.
foreach($_FILES['image'] as $files){echo $files['name']; }
Ничего не получилось, в конечном итоге я хотел пройтись по всем изображениям, убедиться, что они имеют правильный формат, размер и переименовать каждое из них. Но этот простой цикл foreach() показывает, что каким-то образом я даже не могу перебрать массив $_FILES, и count() смутил меня еще больше, когда он говорит, что в массиве 5 элементов, когда я даже ничего не загружал.
11 ответов
Ваша форма примера должна работать нормально. Просто вы ожидаете структуру $_FILES
superglobal должен отличаться от реального, когда используется структура массива для имен полей.
Структура этого многомерного массива следующая:
$_FILES[fieldname] => array(
[name] => array( /* these arrays are the size you expect */ )
[type] => array( /* these arrays are the size you expect */ )
[tmp_name] => array( /* these arrays are the size you expect */ )
[error] => array( /* these arrays are the size you expect */ )
[size] => array( /* these arrays are the size you expect */ )
);
Therefor count( $_FILES[ "fieldname" ] )
даст 5
,
Но подсчет более глубоких измерений также не даст ожидаемого результата. Подсчет полей с count( $_FILES[ "fieldname" ][ "tmp_name" ] )
например, всегда приведет к количеству полей файла, а не к количеству файлов, которые были фактически загружены. Вам все равно придется пройтись по элементам, чтобы определить, было ли загружено что-либо для определенного поля файла.
РЕДАКТИРОВАТЬ
Итак, чтобы пройтись по полям, вы должны сделать что-то вроде следующего:
// !empty( $_FILES ) is an extra safety precaution
// in case the form's enctype="multipart/form-data" attribute is missing
// or in case your form doesn't have any file field elements
if( strtolower( $_SERVER[ 'REQUEST_METHOD' ] ) == 'post' && !empty( $_FILES ) )
{
foreach( $_FILES[ 'image' ][ 'tmp_name' ] as $index => $tmpName )
{
if( !empty( $_FILES[ 'image' ][ 'error' ][ $index ] ) )
{
// some error occured with the file in index $index
// yield an error here
return false; // return false also immediately perhaps??
}
/*
edit: the following is not necessary actually as it is now
defined in the foreach statement ($index => $tmpName)
// extract the temporary location
$tmpName = $_FILES[ 'image' ][ 'tmp_name' ][ $index ];
*/
// check whether it's not empty, and whether it indeed is an uploaded file
if( !empty( $tmpName ) && is_uploaded_file( $tmpName ) )
{
// the path to the actual uploaded file is in $_FILES[ 'image' ][ 'tmp_name' ][ $index ]
// do something with it:
move_uploaded_file( $tmpName, $someDestinationPath ); // move to new location perhaps?
}
}
}
Для получения дополнительной информации см. Документы.
Короткая функция для перестройки $_FILES['files'] в более ожидаемую структуру.
function restructureFilesArray($files)
{
$output = [];
foreach ($files as $attrName => $valuesArray) {
foreach ($valuesArray as $key => $value) {
$output[$key][$attrName] = $value;
}
}
return $output;
}
Просто переименуйте свои поля таким образом
Main photo: <input type="file" name="image1" />
Side photo 1: <input type="file" name="image2" />
Side photo 2: <input type="file" name="image3" />
Side photo 3: <input type="file" name="image4" />
и тогда вы сможете повторить это обычным способом:
foreach($_FILES as $file){
echo $file['name'];
}
Я нашел решение, которое работает с массивами $_FILES произвольной глубины. В качестве быстрого объяснения, что вам нужно алгоритм, который делает это:
For each subtree in the file tree that's more than one item deep:
For each leaf of the subtree:
$leaf[a][b][c] ... [y][z] -> $result[z][a][b][c] ... [y]
Вот код, который на самом деле работает.
function sane_file_array($files) {
$result = array();
$name = array();
$type = array();
$tmp_name = array();
$error = array();
$size = array();
foreach($files as $field => $data) {
foreach($data as $key => $val) {
$result[$field] = array();
if(!is_array($val)) {
$result[$field] = $data;
} else {
$res = array();
files_flip($res, array(), $data);
$result[$field] += $res;
}
}
}
return $result;
}
function array_merge_recursive2($paArray1, $paArray2) {
if (!is_array($paArray1) or !is_array($paArray2)) { return $paArray2; }
foreach ($paArray2 AS $sKey2 => $sValue2) {
$paArray1[$sKey2] = array_merge_recursive2(@$paArray1[$sKey2], $sValue2);
}
return $paArray1;
}
function files_flip(&$result, $keys, $value) {
if(is_array($value)) {
foreach($value as $k => $v) {
$newkeys = $keys;
array_push($newkeys, $k);
files_flip($result, $newkeys, $v);
}
} else {
$res = $value;
// Move the innermost key to the outer spot
$first = array_shift($keys);
array_push($keys, $first);
foreach(array_reverse($keys) as $k) {
// You might think we'd say $res[$k] = $res, but $res starts out not as an array
$res = array($k => $res);
}
$result = array_merge_recursive2($result, $res);
}
}
Просто вызовите sane_files_array для $_FILES, и все будет хорошо, независимо от глубины массива $_FILES. Это действительно должно быть частью самого языка, потому что форматирование массива $_FILES абсолютно нелепо.
Может быть:
Main photo: <input type="file" name="image1" />
Side photo 1: <input type="file" name="image2" />
Side photo 2: <input type="file" name="image3" />
Side photo 3: <input type="file" name="image4" />
$i=1;
while (isset($_FILES['image'.$i])) {
print_r($_FILES['image'.$i]);
$i++;
}
Если вам нужно пройти через определенные поля файла.
Мне нравится попытка @Lendrick восстановить массив. Я полностью согласен с тем, что исходный массив $_FILES совершенно безумен в PHP.
Я придумал эту функцию, которая поддерживает многомерные массивы, такие как
<input type="file" name="a[b][c]" />
/*
* Return a sane list of uploaded files
* @author tim-international.net
*/
function get_uploaded_files() {
$result = [];
foreach (explode('&', http_build_query($_FILES, '&')) as $pair) {
list($key, $value) = explode('=', $pair);
$key = urlencode(preg_replace('#^([^\[]+)\[(name|tmp_name|error|type)\](.*)$#', '$1$3[$2]', urldecode($key)));
$result[] = $key .'='. $value;
}
parse_str(implode('&', $result), $result);
return $result;
}
Пример вывода для
<input type="file" name="image[]" /><input type="file" name="image[]" />
:
array(1) {
["image"]=>
array(1) {
[0]=>
array(5) {
["name"]=>
string(20) "My uploaded file1.png"
["type"]=>
string(9) "image/png"
["tmp_name"]=>
string(27) "C:\Windows\Temp\php6A8E1.tmp"
["error"]=>
int(0)
["size"]=>
int(26570)
}
[1]=>
array(5) {
["name"]=>
string(20) "My uploaded file2.png"
["type"]=>
string(9) "image/png"
["tmp_name"]=>
string(27) "C:\Windows\Temp\php6A8E2.tmp"
["error"]=>
int(0)
["size"]=>
int(26570)
}
}
}
Примеры использования:
$uploaded = get_uploaded_files();
foreach ($uploaded['image'] as $i => $file) {
move_uploaded_file($uploaded[$i]['tmp_name'], ...);
}
Выбор PHP того, как обрабатывать $_FILES, тратит много времени на разработку. Основываясь на ответе @Lendrick, вот аналогичный ОО подход.
/**
* @brief get the POSTed files in a more usable format
Works on the following methods:
<form method="post" action="/" name="" enctype="multipart/form-data">
<input type="file" name="photo1" />
<input type="file" name="photo2[]" />
<input type="file" name="photo2[]" />
<input type="file" name="photo3[]" multiple />
* @return Array
* @todo
* @see http://stackru.com/questions/5444827/how-do-you-loop-through-files-array
*/
public static function GetPostedFiles()
{
/* group the information together like this example
Array
(
[attachments] => Array
(
[0] => Array
(
[name] => car.jpg
[type] => image/jpeg
[tmp_name] => /tmp/phpe1fdEB
[error] => 0
[size] => 2345276
)
)
[jimmy] => Array
(
[0] => Array
(
[name] => 1.jpg
[type] => image/jpeg
[tmp_name] => /tmp/phpx1HXrr
[error] => 0
[size] => 221041
)
[1] => Array
(
[name] => 2 ' .jpg
[type] => image/jpeg
[tmp_name] => /tmp/phpQ1clPh
[error] => 0
[size] => 47634
)
)
)
*/
$Result = array();
$Name = array();
$Type = array();
$TmpName = array();
$Error = array();
$Size = array();
foreach($_FILES as $Field => $Data)
{
foreach($Data as $Key => $Val)
{
$Result[$Field] = array();
if(!is_array($Val))
$Result[$Field] = $Data;
else
{
$Res = array();
self::GPF_FilesFlip($Res, array(), $Data);
$Result[$Field] += $Res;
}
}
}
return $Result;
}
private static function GPF_ArrayMergeRecursive($PaArray1, $PaArray2)
{
// helper method for GetPostedFiles
if (!is_array($PaArray1) or !is_array($PaArray2))
return $PaArray2;
foreach ($PaArray2 AS $SKey2 => $SValue2)
$PaArray1[$SKey2] = self::GPF_ArrayMergeRecursive(@$PaArray1[$SKey2], $SValue2);
return $PaArray1;
}
private static function GPF_FilesFlip(&$Result, $Keys, $Value)
{
// helper method for GetPostedFiles
if(is_array($Value))
{
foreach($Value as $K => $V)
{
$NewKeys = $Keys;
array_push($NewKeys, $K);
self::GPF_FilesFlip($Result, $NewKeys, $V);
}
}
else
{
$Res = $Value;
// move the innermost key to the outer spot
$First = array_shift($Keys);
array_push($Keys, $First);
foreach(array_reverse($Keys) as $K)
$Res = array($K => $Res); // you might think we'd say $Res[$K] = $Res, but $Res starts out not as an array
$Result = self::GPF_ArrayMergeRecursive($Result, $Res);
}
}
Я очень опаздываю на этот ответ, но я устал решать проблему с массивом файлов PHP снова и снова, поэтому я написал пакет композитора, чтобы мне больше никогда не пришлось. Может, кто-нибудь в гугле найдет мой ответ и будет счастлив.
Установить tvanc/files-array-organizer
composer require tvanc/files-array-organizer
Проходить $_FILES
к нему, и он вернет вам массив, который структурирован так, как вы ожидали.
<?php
use tvanc\FilesArrayOrganizer\FilesArrayOrganizer;
require 'vendor/autoload.php';
if ($_FILES) {
$organizedFiles = FilesArrayOrganizer::organize($_FILES);
// Now you can foreach over your files
foreach($organizedFiles['image'] as $file){
echo $file['name'];
}
}
?>
Main photo: <input type="file" name="image[]" />
Side photo 1: <input type="file" name="image[]" />
Side photo 2: <input type="file" name="image[]" />
Side photo 3: <input type="file" name="image[]" />
Объединение временного пути с именем файла, в результате чего получится массив типа:
array(2) {
["/tmp/phpAYCvcc"]=> string(10) "file1.jpg"
["/tmp/phpCDg79o"]=> string(10) "file2.jpg"
}
Код:
$files = array_combine(
$_FILES['receipt']['tmp_name'],
$_FILES['receipt']['name']
);
foreach ($files as $key => $value) {
// save your files locally
}
Я боролся с этой дилеммой почти неделю! Ничто из того, что я нашел в сети, не могло мне помочь. Я знал, что делать, но не мог понять, как правильно перебрать массив $_FILES - до тех пор, пока не прочитал отредактированный пост с принятым ответом.
Я внес некоторые изменения в сценарий, который был опубликован, поскольку он не работал должным образом для меня. Я хотел иметь возможность определить, был ли файл вообще выбран, поэтому я изменил строку "if(! Empty( $_FILES[ 'image' ][ 'error' ][ $index ]))" на "if(! пусто ( $_FILES[ 'image' ][ 'size' ][ $index ]))", а затем вместо" return false; "я вместо этого помещаю размер в переменную: "$Size = $_FILES[ 'upload' " ][ 'size' ][ $index ];"
Таким образом, я мог проверить, была ли переменная $ Size больше нуля. Если это так, то был выбран файл, и я мог бы продолжить подсчет количества файлов и выполнить фактическую загрузку. Я не использовал ни одного "ненужного" скрипта после "return false;" в принятом ответе. Надеюсь, это кому-нибудь поможет.
: P / MacD
Если вы не можете присоединиться к ним, побейте их.
https://gist.github.com/noorwachid/fce70a3a2d96502c2805248c46fc23f9
<?php
class UploadedFile
{
public static function walk(&$originalPath, &$path, &$contentType, &$size, &$errorCode)
{
if (is_array($originalPath)) {
foreach ($originalPath as $key => &$value)
self::walk($value, $path[$key], $contentType[$key], $size[$key], $errorCode[$key]);
} else {
$originalPath = [
'originalPath' => $originalPath,
'path' => $path,
'contentType' => $contentType,
'size' => $size,
'errorCode' => $errorCode,
];
}
}
public static function build()
{
// swap second keys
$rootNode = [];
foreach ($_FILES as $key => $value) {
foreach ($value as $key2 => $value2) {
$rootNode[$key2][$key] = $value2;
}
}
// swap first and last keys
self::walk($rootNode['name'], $rootNode['tmp_name'], $rootNode['type'], $rootNode['size'], $rootNode['error']);
// remove unused keys
unset($rootNode['tmp_name']);
unset($rootNode['type']);
unset($rootNode['size']);
unset($rootNode['error']);
return $rootNode['name'];
}
}
// original
print_r($_FILES);
// restructured
print_r(UploadedFile::build());
// original
Array
(
[one] => Array
(
[name] => ss_break.png
[type] => image/png
[tmp_name] => /tmp/phppY8lSV
[error] => 0
[size] => 43582
)
[multiple] => Array
(
[name] => Array
(
[0] => LeleBreeder.png
)
[type] => Array
(
[0] => image/png
)
[tmp_name] => Array
(
[0] => /tmp/php6GSj1X
)
[error] => Array
(
[0] => 0
)
[size] => Array
(
[0] => 14284
)
)
[crazy] => Array
(
[name] => Array
(
[nested] => Array
(
[keys] => PlainUbuntu.png
)
)
[type] => Array
(
[nested] => Array
(
[keys] => image/png
)
)
[tmp_name] => Array
(
[nested] => Array
(
[keys] => /tmp/php4boHkj
)
)
[error] => Array
(
[nested] => Array
(
[keys] => 0
)
)
[size] => Array
(
[nested] => Array
(
[keys] => 25668
)
)
)
)
// restructured
Array
(
[one] => Array
(
[originalPath] => ss_break.png
[path] => /tmp/phppY8lSV
[contentType] => image/png
[size] => 43582
[errorCode] => 0
)
[multiple] => Array
(
[0] => Array
(
[originalPath] => LeleBreeder.png
[path] => /tmp/php6GSj1X
[contentType] => image/png
[size] => 14284
[errorCode] => 0
)
)
[crazy] => Array
(
[nested] => Array
(
[keys] => Array
(
[originalPath] => PlainUbuntu.png
[path] => /tmp/php4boHkj
[contentType] => image/png
[size] => 25668
[errorCode] => 0
)
)
)
)