如何循环遍历 $_FILES 数组?

问题描述 投票:0回答:11

这是我想要循环的输入

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 个元素时,让我更加困惑。

php loops input foreach form-processing
11个回答
46
投票

您的示例表单应该可以正常工作。只是当使用数组结构作为字段名称时,您期望

$_FILES
超全局的结构与实际不同。

这个多维数组的结构如下:

$_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 */ )
);

因此

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?
        }
    }
}

有关更多信息,请参阅文档


15
投票

将 $_FILES['files'] 重建为更预期的结构的短函数。

function restructureFilesArray($files)
{
    $output = [];
    foreach ($files as $attrName => $valuesArray) {
        foreach ($valuesArray as $key => $value) {
            $output[$key][$attrName] = $value;
        }
    }
    return $output;
}

13
投票

只需这样重命名您的字段即可

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

5
投票

我想出了一个适用于任意深度的 $_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);
  }
}

只要在 $_FILES 上调用 sane_files_array 就可以了,无论 $_FILES 数组有多深。这确实应该是语言本身的一部分,因为 $_FILES 数组的格式绝对是荒谬的。


3
投票

也许:

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

如果您必须循环遍历特定文件字段。


2
投票

PHP 选择如何处理 $_FILES 浪费了开发人员大量时间。基于@Lendrick的回答,这里有一个类似的OO方法。

/**
* @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://stackoverflow.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);
    }
}

1
投票

对于这个答案,我已经迟到了,但我厌倦了一遍又一遍地解决处理 PHP 文件数组的问题,所以我编写了一个 Composer 包,这样我就再也不用这样做了。也许有人在谷歌上搜索会找到我的答案并感到高兴。

安装

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[]" />

1
投票

我喜欢@Lendrick 重建数组的尝试。我完全同意 PHP 中原始的 $_FILES 数组纯粹是疯狂的。

替代方案1

我想出了这个支持多维数组的函数,例如

<input type="file" name="a[b][c]" />

  /*
   * Return a sane list of uploaded files
   * @author tim-international.net
   */
  function get_uploaded_files() {

    $result = [];
    foreach (preg_split('#&#', http_build_query($_FILES, '&'), -1, PREG_SPLIT_NO_EMPTY) as $pair) {
      list($key, $value) = explode('=', $pair);
      $key = urlencode(preg_replace('#^([^\[]+)\[(name|tmp_name|type|size|error)\](.*)$#', '$1$3[$2]', urldecode($key)));
      $result[] = $key .'='. $value;
    }

    parse_str(implode('&', $result), $result);

    return $result;
  }

<input type="file" name="image[]" multiple />
的示例输出:

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

替代方案2

另一个变体可以提供更扁平的数组,可以方便地使用:

  function get_uploaded_files() {

    $result = [];

    foreach (preg_split('#&#', http_build_query($_FILES, '&'), -1, PREG_SPLIT_NO_EMPTY) as $pair) {
      list($key, $value) = explode('=', $pair);
      
      if (preg_match('#^([^\[]+)\[(name|tmp_name|type|size|error)\](.*)$#', urldecode($key), $matches)) {
        $result[$matches[1].$matches[3]][$matches[2]] = urldecode($value);
      }
    }

    return $result;
  }

这将返回以下

<input type="file" name="foo[bar][]" multiple />

array(1) {
  ["foo[bar][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)
    }
    ["foo[bar][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)
    }
  }
}

使用示例:

foreach (get_uploaded_files() as $field => $file) {
  move_uploaded_file($file['tmp_name'], ...);
}

0
投票

我在这个困境中挣扎了将近一周!我在网上找到的任何东西都无法帮助我。我知道该怎么做,但无法弄清楚如何正确循环 $_FILES 数组 - 直到现在我阅读了已接受答案的编辑后的帖子。

不过,我在发布的脚本中做了一些更改,因为它对我来说不能正常工作。我希望能够确定是否选择了文件,所以我更改了该行 "if( !empty( $_FILES[ 'image' ][ 'error' ][ $index ] ) )" 到 "if( !empty( $_FILES[ 'image' ][ 'size' ][ $index ] ) )" 然后我将大小放入变量中,而不是“return false;”: “$Size = $_FILES['上传']['大小'][$index];”

这样我可以检查 $Size 变量是否大于零。如果是,则已选择一个文件,我可以继续计算文件数量并进行实际上传。在接受的答案中,我没有在“return false;”之后使用任何“不必要的”脚本。希望这对某人有帮助。

:P /MacD


0
投票

如果你不能加入他们,就打败他们。

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
                        )

                )

        )

)

0
投票

将临时路径与文件名组合,将产生一个如下数组:

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
}
© www.soinside.com 2019 - 2024. All rights reserved.