アップロードされたファイルの操作

CodeIgniter では、フォームからアップロードされたファイルの操作が、PHP の $_FILES 配列を直接使用するよりもはるかに簡単かつ安全になります。これは File クラスを拡張したものであり、そのクラスのすべての機能を利用できます。

注意

これは CodeIgniter v3.x のファイルアップロードクラスと同じではありません。これは、いくつかの小さな機能を備えたアップロードされたファイルへの生のインターフェースを提供します。

プロセス

ファイルのアップロードには、次の一般的なプロセスが含まれます。

  • アップロードフォームが表示され、ユーザーはファイルを選択してアップロードできます。

  • フォームが送信されると、ファイルは指定した場所にアップロードされます。

  • その過程で、ファイルは設定した設定に基づいてアップロードが許可されていることを確認するために検証されます。

  • アップロードが完了すると、ユーザーに成功メッセージが表示されます。

このプロセスを示すために、簡単なチュートリアルを示します。その後、参考情報を見つけることができます。

アップロードフォームの作成

テキストエディターを使用して、**upload_form.php** というフォームを作成します。その中にこのコードを配置し、**app/Views/** ディレクトリに保存します

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Upload Form</title>
</head>
<body>

<?php foreach ($errors as $error): ?>
    <li><?= esc($error) ?></li>
<?php endforeach ?>

<?= form_open_multipart('upload/upload') ?>
    <input type="file" name="userfile" size="20">
    <br><br>
    <input type="submit" value="upload">
</form>

</body>
</html>

フォームヘルパーを使用して、開始フォームタグを作成していることがわかります。ファイルアップロードにはマルチパートフォームが必要なため、ヘルパーが適切な構文を作成します。また、$errors 変数があることにも気づくでしょう。これは、ユーザーが何か間違った操作をした場合にエラーメッセージを表示できるようにするためです。

成功ページ

テキストエディターを使用して、**upload_success.php** というフォームを作成します。その中にこのコードを配置し、**app/Views/** ディレクトリに保存します

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Upload Form</title>
</head>
<body>

<h3>Your file was successfully uploaded!</h3>

<ul>
    <li>name: <?= esc($uploaded_fileinfo->getBasename()) ?></li>
    <li>size: <?= esc($uploaded_fileinfo->getSizeByUnit('kb')) ?> KB</li>
    <li>extension: <?= esc($uploaded_fileinfo->guessExtension()) ?></li>
</ul>

<p><?= anchor('upload', 'Upload Another File!') ?></p>

</body>
</html>

コントローラー

テキストエディターを使用して、**Upload.php** というコントローラーを作成します。その中にこのコードを配置し、**app/Controllers/** ディレクトリに保存します

<?php

namespace App\Controllers;

use CodeIgniter\Files\File;

class Upload extends BaseController
{
    protected $helpers = ['form'];

    public function index()
    {
        return view('upload_form', ['errors' => []]);
    }

    public function upload()
    {
        $validationRule = [
            'userfile' => [
                'label' => 'Image File',
                'rules' => [
                    'uploaded[userfile]',
                    'is_image[userfile]',
                    'mime_in[userfile,image/jpg,image/jpeg,image/gif,image/png,image/webp]',
                    'max_size[userfile,100]',
                    'max_dims[userfile,1024,768]',
                ],
            ],
        ];
        if (! $this->validate($validationRule)) {
            $data = ['errors' => $this->validator->getErrors()];

            return view('upload_form', $data);
        }

        $img = $this->request->getFile('userfile');

        if (! $img->hasMoved()) {
            $filepath = WRITEPATH . 'uploads/' . $img->store();

            $data = ['uploaded_fileinfo' => new File($filepath)];

            return view('upload_success', $data);
        }

        $data = ['errors' => 'The file has already been moved.'];

        return view('upload_form', $data);
    }
}

注意

ファイルアップロード HTML フィールドの値は存在せず、$_FILES グローバルに保存されるため、ファイルアップロードのルールのみを バリデーションでアップロードファイルを検証するために使用できます。ルール required も使用できないため、代わりに uploaded を使用してください。

ルート

テキストエディターを使用して、**app/Config/Routes.php** を開きます。その中に次の 2 つのルートを追加します

<?php

// ...

/*
 * --------------------------------------------------------------------
 * Route Definitions
 * --------------------------------------------------------------------
 */

// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'Home::index');

$routes->get('upload', 'Upload::index');          // Add this line.
$routes->post('upload/upload', 'Upload::upload'); // Add this line.

// ...

アップロードディレクトリ

アップロードされたファイルは、**writable/uploads/** ディレクトリに保存されます。

試してみよう!

フォームを試すには、次のような URL を使用してサイトにアクセスします

example.com/index.php/upload/

アップロードフォームが表示されます。画像ファイル(**jpg**、**gif**、**png**、または **webp**)をアップロードしてみてください。コントローラーのパスが正しい場合は、正常に機能するはずです。

ファイルへのアクセス

すべてのファイル

ファイルをアップロードすると、PHP では $_FILES スーパーグローバルを介してネイティブにアクセスできます。この配列には、複数のファイルを一度にアップロードする場合に大きな欠点があり、多くの開発者が認識していない潜在的なセキュリティ上の欠陥があります。CodeIgniter は、共通のインターフェイスの背後でファイルの利用を標準化することにより、これらの両方の状況に対応します。

ファイルには、現在の IncomingRequest インスタンスを介してアクセスします。このリクエストでアップロードされたすべてのファイルを取得するには、getFiles() を使用します。これにより、CodeIgniter\HTTP\Files\UploadedFile のインスタンスで表されるファイルの配列が返されます。

<?php

$files = $this->request->getFiles();

もちろん、ファイル入力を命名する方法は複数あり、最も単純なもの以外は奇妙な結果を引き起こす可能性があります。配列は、期待どおりの方法で返されます。最も単純な使用法では、単一のファイルが次のように送信される可能性があります。

<input type="file" name="avatar">

これにより、次のような単純な配列が返されます。

[
    'avatar' => // UploadedFile instance,
];

注意

UploadedFile インスタンスは $_FILES に対応します。ユーザーが送信ボタンをクリックしてファイルをアップロードしなくても、インスタンスは引き続き存在します。UploadedFile の isValid() メソッドを使用して、ファイルが実際にアップロードされたかどうかを確認できます。ファイルの検証 を参照してください。

name に配列表記を使用した場合、入力は次のようになります。

<input type="file" name="my-form[details][avatar]">

getFiles() によって返される配列は、次のようになります。

[
     'my-form' => [
        'details' => [
            'avatar' => // UploadedFile instance
        ],
    ],
]

場合によっては、アップロードするファイルの配列を指定する場合があります。

Upload an avatar: <input type="file" name="my-form[details][avatars][]">
Upload an avatar: <input type="file" name="my-form[details][avatars][]">

この場合、返されるファイルの配列は次のようになります。

[
    'my-form' => [
        'details' => [
            'avatar' => [
                0 => // UploadedFile instance,
                1 => // UploadedFile instance,
            ],
        ],
    ],
]

単一ファイル

単一のファイルにアクセスする必要がある場合は、getFile() を使用してファイルインスタンスを直接取得できます。これにより、CodeIgniter\HTTP\Files\UploadedFile のインスタンスが返されます。

最もシンプルな使用法

最も単純な使用法では、単一のファイルが次のように送信される可能性があります。

<input type="file" name="userfile">

これにより、次のような単純なファイルインスタンスが返されます。

<?php

$file = $this->request->getFile('userfile');

配列表記

name に配列表記を使用した場合、入力は次のようになります。

<input type="file" name="my-form[details][avatar]">

ファイルインスタンスを取得するには

<?php

$file = $this->request->getFile('my-form.details.avatar');

複数ファイル

<input type="file" name="images[]" multiple>

コントローラー

<?php

if ($imagefile = $this->request->getFiles()) {
    foreach ($imagefile['images'] as $img) {
        if ($img->isValid() && ! $img->hasMoved()) {
            $newName = $img->getRandomName();
            $img->move(WRITEPATH . 'uploads', $newName);
        }
    }
}

ここで、images はフォームフィールド名からのループです。

同じ名前のファイルが複数ある場合は、getFile() を使用してすべてのファイルを個別に取得できます。

コントローラー

<?php

$file1 = $this->request->getFile('images.0');
$file2 = $this->request->getFile('images.1');

getFileMultiple() を使用すると、同じ名前のアップロードされたファイルの配列を簡単に取得できます。

<?php

$files = $this->request->getFileMultiple('images');

別の例

Upload an avatar: <input type="file" name="my-form[details][avatars][]">
Upload an avatar: <input type="file" name="my-form[details][avatars][]">

コントローラー

<?php

$file1 = $this->request->getFile('my-form.details.avatars.0');
$file2 = $this->request->getFile('my-form.details.avatars.1');

注意

getFiles() を使用する方が適切です。

ファイルの操作

UploadedFile インスタンスを取得したら、ファイルを安全な方法で取得したり、ファイルを新しい場所に移動したりできます。

ファイルの検証

isValid() メソッドを呼び出すことで、ファイルが HTTP を介してエラーなしで実際にアップロードされたことを確認できます。

<?php

if (! $file->isValid()) {
    throw new \RuntimeException($file->getErrorString() . '(' . $file->getError() . ')');
}

この例に見られるように、ファイルのアップロードエラーが発生した場合、getError() メソッドと getErrorString() メソッドを使用して、エラーコード(整数)とエラーメッセージを取得できます。このメソッドで検出できるエラーは次のとおりです。

  • ファイルが upload_max_filesize ini ディレクティブを超えています。

  • ファイルがフォームで定義されたアップロード制限を超えています。

  • ファイルは部分的にしかアップロードされませんでした。

  • ファイルはアップロードされませんでした。

  • ファイルをディスクに書き込めませんでした。

  • ファイルのアップロードに失敗しました:一時ディレクトリが見つかりません。

  • PHP拡張機能によってファイルのアップロードが停止しました。

ファイル名

getName()

getName() メソッドを使用すると、クライアントから提供された元のファイル名を取得できます。これは通常、クライアントから送信されたファイル名であり、信頼すべきではありません。ファイルが移動された場合、これは移動後の最終的なファイル名を返します。

<?php

$name = $file->getName();

getClientName()

ファイルが移動された場合でも、クライアントから送信された元のアップロードファイル名を常に返します。

<?php

$originalName = $file->getClientName();

getTempName()

アップロード中に作成された一時ファイルのフルパスを取得するには、getTempName() メソッドを使用できます。

<?php

$tempfile = $file->getTempName();

その他のファイル情報

getClientExtension()

アップロードされたファイル名に基づいて、元のファイル拡張子を返します。

<?php

$ext = $file->getClientExtension();

警告

これは信頼できるソースではありません。信頼できるバージョンについては、代わりに guessExtension() を使用してください。

getClientMimeType()

クライアントから提供されたファイルの MIME タイプ(MIME タイプ)を返します。これは信頼できる値ではありません。信頼できるバージョンについては、代わりに getMimeType() を使用してください。

<?php

$type = $file->getClientMimeType();

echo $type; // image/png

getClientPath()

バージョン 4.4.0 で新しく追加されました。

クライアントがディレクトリアップロード経由でファイルをアップロードした場合、アップロードされたファイルの webkit 相対パスを返します。PHP 8.1 より前のバージョンでは、これは null を返します。

<?php

$clientPath = $file->getClientPath();
echo $clientPath; // dir/file.txt, or dir/sub_dir/file.txt

ファイルの移動

元のファイル名で

各ファイルは、適切に名付けられた move() メソッドを使用して、新しい場所に移動できます。これは、ファイルの移動先となるディレクトリを最初のパラメーターとして受け取ります。

<?php

$file->move(WRITEPATH . 'uploads');

デフォルトでは、元のファイル名が使用されます。

新しいファイル名で

2番目のパラメータとして新しいファイル名を指定できます。

<?php

$newName = $file->getRandomName();
$file->move(WRITEPATH . 'uploads', $newName);

既存のファイルを上書きする

デフォルトでは、宛先ファイルが既に存在する場合、新しいファイル名が使用されます。たとえば、ディレクトリに **image_name.jpg** が既に存在する場合、ファイル名は自動的に **image_name_1.jpg** になります。

3番目のパラメーターとして true を渡すことで、既存のファイルを上書きできます。

<?php

$file->move(WRITEPATH . 'uploads', null, true);

ファイルが移動されたかどうかを確認する

ファイルが削除されると、一時ファイルは削除されます。ファイルがすでに移動されているかどうかは、ブール値を返す hasMoved() メソッドで確認できます。

<?php

if ($file->isValid() && ! $file->hasMoved()) {
    $file->move($path);
}

移動が失敗した場合

アップロードされたファイルの移動は、いくつかの状況下で HTTPException を伴って失敗する可能性があります。

  • ファイルはすでに移動されている

  • ファイルのアップロードが正常に行われなかった

  • ファイル移動操作が失敗した場合(例:不適切なアクセス許可)

ファイルの保存

各ファイルは、適切に名付けられた store() メソッドを使用して、新しい場所に移動できます。

最も単純な使用法では、単一のファイルが次のように送信される可能性があります。

<input type="file" name="userfile">

デフォルトでは、アップロードされたファイルは **writable/uploads** ディレクトリに保存されます。 **YYYYMMDD** フォルダとランダムなファイル名が作成されます。ファイルパスを返します。

<?php

$path = $this->request->getFile('userfile')->store();

ファイルの移動先となるディレクトリを最初のパラメーターとして指定できます。2番目のパラメーターとして新しいファイル名を渡すことができます。

<?php

$path = $this->request->getFile('userfile')->store('head_img/', 'user_name.jpg');

アップロードされたファイルの移動は、いくつかの状況下で HTTPException を伴って失敗する可能性があります。

  • ファイルはすでに移動されている

  • ファイルのアップロードが正常に行われなかった

  • ファイル移動操作が失敗した場合(例:不適切なアクセス許可)