web-dev-qa-db-ja.com

$ _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つのファイルしかないのに、追加の入力が1つあるのはなぜですか?

今度は実際にループするので、foreachループを使用してみますが、機能しません。

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

何も思いつきませんでした。最終的に私がやりたかったのは、すべての画像をループし、正しい形式、サイズであることを確認し、それぞれの名前を変更することです。しかし、この単純なforeach()ループは、何とか$ _FILES配列をループすることさえできず、何もアップロードしなかったときに配列に5つの要素があると言うと、count()がさらに混乱したことを示しています。

19
Ben

サンプルフォームは問題なく機能するはずです。フィールド名に配列構造を使用する場合、_$_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 を参照してください。

42
Decent Dabbler

このようにフィールドの名前を変更するだけです

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

$ _FILES ['files']をより期待される構造に再構築するための短い関数。

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

任意の深さの$ _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
Lendrick

多分:

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
woodgate

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

私はこの答えのためにゲームにかなり遅れていますが、PHP files arrayを何度も処理する問題を解決するのに飽きたので、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[]" />
0
tvanc

私はこのジレンマとほぼ一週間苦労しています!ネットで見つけたものは何の助けにもなりません。私は何をすべきかを知っていましたが、$ _ FILES配列を適切にループする方法を理解することができませんでした。

ただし、投稿されたスクリプトでは、私には適切に機能しなかったため、いくつか変更を加えました。ファイルが選択されたかどうかを判断できるようにしたいので、「if(!empty($ _FILES ['image'] ['error'] [$ index]))」という行を「if(! empty($ _FILES ['image'] ['size'] [$ index])) "そして" return false; "の代わりに、サイズを変数に入れます:" $ Size = $ _FILES ['upload' ] ['size'] [$ index]; "

このようにして、$ Size変数がゼロより大きいかどうかを確認できます。そうであれば、ファイルが選択されていたので、ファイル数のカウントを続行して実際のアップロードを行うことができました。受け入れられた回答では、「return false;」の後に「不要な」スクリプトを使用しませんでした。これが誰かを助けることを願っています。

:P/MacD

0
MacDknife