你如何循环$ _FILES数组?

时间:2011-03-26 19:41:21

标签: php loops input foreach form-processing

以下是我想要循环的输入

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数组,并且当我甚至没有上传任何内容时,当它说数组中有5个元素时,count()会让我更加困惑。

7 个答案:

答案 0 :(得分:41)

您的示例表单应该可以正常工作。只是当你对字段名称使用数组结构时,你期望$_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?
        }
    }
}

有关详细信息,请参阅the docs

答案 1 :(得分:11)

只需按照这种方式重命名字段

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

答案 2 :(得分:3)

我想出了一个适用于任意深度的$ _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 :(得分:2)

也许:

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

如果必须遍历特定的文件字段。

答案 4 :(得分: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[]" />

答案 5 :(得分:0)

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://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);
    }
}

答案 6 :(得分:0)

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

我在发布的脚本中做了一些更改,因为它对我来说不起作用。我希望能够确定是否完全选择了一个文件,所以我更改了该行 &#34; if(!empty($ _FILES [&#39; image&#39;] [&#39; error&#39;] [$ index]))&#34;  至 &#34; if(!empty($ _FILES [&#39; image&#39;] [&#39; size&#39;] [$ index]))&#34; 然后代替&#34;返回false;&#34;,我将大小放入变量中: &#34; $ size = $ _FILES [&#39;上传&#39; ] [&#39;尺寸&#39; ] [$ index];&#34;

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

:P /霍尔德