web-dev-qa-db-fra.com

Comment passez-vous en boucle dans le tableau $ _FILES?

Voici les entrées que je veux parcourir 

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

Quelques événements étranges se sont produits. Lorsque je n’ai rien transféré, j’utilise la fonction count($_FILES['image']) et renvoie cette fonction. Cette fonction renvoie la valeur 5. Il ne doit y avoir aucun élément dans ce tableau. Pourquoi y a-t-il une entrée supplémentaire alors que je n'ai que 4 fichiers pour commencer?

Maintenant, avec la boucle elle-même, j'essaie d'utiliser la boucle foreach, mais cela ne fonctionne pas.

foreach($_FILES['image'] as $files){echo $files['name']; }

En fin de compte, ce que je voulais faire, c’est de passer en revue toutes les images, de s’assurer qu’elles ont le format et la taille corrects, et de renommer chacune d’elles. Mais cette simple boucle foreach () montre que, d'une manière ou d'une autre, je ne peux même pas parcourir le tableau $ _FILES et que count () m'a encore plus gêné lorsqu'il indique qu'il y a 5 éléments dans le tableau alors que je n'ai même rien importé.

18
Ben

Votre exemple de formulaire devrait bien fonctionner. Vous vous attendez simplement à ce que la structure du $_FILES superglobal soit différente de ce qu'elle est réellement, lorsque vous utilisez une structure de tableau pour les noms de champ.

La structure de ce tableau multidimensionnel est la suivante:

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

Ainsi, count( $_FILES[ "fieldname" ] ) donnera 5.
Mais compter des dimensions plus profondes ne produira pas le résultat escompté. Compter les champs avec count( $_FILES[ "fieldname" ][ "tmp_name" ] ) par exemple, aura toujours pour résultat le nombre de champs de fichier, pas le nombre de fichiers qui ont été réellement téléchargés. Vous devez toujours parcourir les éléments pour déterminer si des éléments ont été téléchargés pour un champ de fichier particulier.

MODIFIER
Donc, pour parcourir les champs vous feriez quelque chose comme ceci:

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

Pour plus d'informations, voir la documentation .

41
Decent Dabbler

il suffit de renommer vos champs de cette façon

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

et ensuite vous pourrez l'itérer de la manière habituelle:

foreach($_FILES as $file){
  echo $file['name']; 
}
11
Your Common Sense

Fonction abrégée pour reconstruire $ _FILES ['fichiers'] dans une structure plus attendue.

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

Je suis venu avec une solution qui fonctionne pour les tableaux $ _FILES de profondeur arbitraire. En guise d’explication rapide, vous avez besoin d’un algorithme qui le fasse:

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]

Voici un code qui fonctionne réellement. 

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

Appelez simplement sane_files_array sur $ _FILES et vous devriez pouvoir continuer, quelle que soit la profondeur du tableau $ _FILES. Cela devrait vraiment faire partie du langage lui-même, car la mise en forme du tableau $ _FILES est absolument ridicule.

3
Lendrick

Peut être:

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

Si vous devez parcourir des champs de fichier spécifiques.

2
woodgate

Le choix de PHP de gérer $ _FILES fait perdre beaucoup de temps aux développeurs. Basé sur la réponse de @ Lendrick, voici une approche similaire 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);
    }
}
0
carrucha

J'ai lutté avec ce dilemme pendant presque une semaine! Rien que j'ai trouvé sur le net pourrait m'aider. Je savais en quelque sorte ce qu'il fallait faire, mais je ne pouvais pas comprendre comment parcourir correctement le tableau $ _FILES - jusqu'à maintenant, lorsque j'ai lu le message modifié de la réponse acceptée.

J'ai cependant apporté quelques modifications au script tel que posté, car il ne fonctionnait pas correctement pour moi. Je voulais être capable de déterminer si un fichier était sélectionné, alors j'ai changé la ligne "If (! Empty ($ _FILES ['image'] ['erreur'] [$ index]))" to "if (! empty ($ _FILES ['image'] [" taille "] [$ index]))" et au lieu de "return false;", je mets la taille dans une variable : "$ Size = $ _FILES ['upload'] ['size'] [$ index];"

De cette façon, je pourrais vérifier si la variable $ Size était supérieure à zéro. Si c'était le cas, un fichier avait été sélectionné et je pouvais continuer à compter le nombre de fichiers et effectuer le téléchargement proprement dit. Je n'ai utilisé aucun des scripts "inutiles" après "return false" dans la réponse acceptée. J'espère que ça aide quelqu'un.

: P/MacD

0
MacDknife