【PHP】容量の大きいファイルをダウンロード処理するとメモリ不足エラーが発生!解決方法を紹介

【PHP】容量の大きいファイルをダウンロード処理するとメモリ不足エラーが発生!解決方法を紹介
2021年12月13日2023年10月14日

PHPでダウンロード処理を書く際に用いられる関数としてreadfile()を使った実装方法を紹介しているサイトが多いです。

最初のころは「おぉ~できた!」となるのですが、容量が大きいファイルをダウンロードしようとするとメモリ不足のエラーが発生して「チーン」となります。

色々調べてみるとreadfile()を使ってもメモリ不足エラーを出さずに正常にダウンロードできる方法を見つけたので紹介します。

メモリ不足エラーが表示されない書き方

さっそくですが、解決方法を先に記載します。

downloadFile("/home/test/files.csv");

/**
 * 
 * 指定したファイルをダウンロードするメソッド
 *
 * @param string $filePathName ダウンロードファイル
 */
function downloadFile($filePathName)
{
    // ダウンロードするファイルが存在するか確認
    if (!file_exists($filePathName)) {
        throw new Exception('ファイルが無いよ。');
    }

    // ファイルの種類を取得
    $mimeType = (new finfo(FILEINFO_MIME_TYPE))->file($filePathName);

    // ファイル種類を設定
    header('Content-Type: ' . $mimeType);

    // Content-Type属性で判断してもらうように設定
    header('X-Content-Type-Options: nosniff');

    // ダウンロードするファイルサイズを設定
    header('Content-Length: ' . filesize($filePathName));

    // ダウンロード時のファイル名を設定
    header('Content-Disposition: attachment; filename="' . basename($filePathName) . '"');

    // keep-aliveを無効にする設定
    header('Connection: close');

    // readfile()の前に必ず記述する
    // バッファリングを無効化する(これが無いとメモリ不足エラーが発生する)
    while (ob_get_level()) {
        ob_end_clean();
    }

    // 出力処理
    readfile($filePathName);

    // 処理終了
    exit;
}


何も考えずにreadfile()を使うと、下記のようなメモリ不足エラーが発生します。

Fatal error: Allowed memory size of 268435456 bytes exhausted

メモリを増やせる環境であればPHPの使用メモリを増やすことで対応可能ですが、簡単にメモリを増やせない環境もあると思います。

readfile関数のPHPマニュアルを読むと、下記の記述があります。

readfile() 自体にはメモリに関する問題はなく、 巨大なファイルを送ってもかまいません。out of memoryエラーが出る場合は、 ob_get_level() で出力バッファリングを無効にしてください。

引用:PHP: readfile - Manual

公式サイトで紹介されていますが、そのままreadfile() を使用するとメモリ不足が発生しますがob_get_level() で出力用バッファを無効化してくださいと書かれています。

つまり、readfile() を呼び出す前に下記を追加するだけでメモリを食い潰さなくなりメモリ不足エラーも発生しなくなります。

while (ob_get_level()) {
    ob_end_clean();
}

まとめ

今回大量のデータをダウンロードしようとするとメモリ不足エラーが発生する現象が発生しました。

データが膨大にならないシステムであれば気にすることはないですが、容量が大きいファイルをダウンロードする機能を実装する際は今回紹介した方法を試してみてください。

コメント

コメントを残す

お名前(任意)
コメント:新規