PHPで中断したファイルアップロードを再開する方法:実装手順と解説

ファイルアップロードは多くのWebアプリケーションにおいて重要な機能であり、ユーザーが画像やドキュメント、動画などをサーバーにアップロードできる環境を提供します。しかし、アップロード中にインターネット接続が不安定になるなどで中断することがあり、途中で中断されたファイルの再アップロードはユーザーにとって負担が大きいです。本記事では、PHPでファイルアップロードの再開機能を実装するための具体的な手順を解説します。ファイルをチャンクに分けてアップロードし、途中から再開できる仕組みを構築することで、ユーザー体験を向上させると同時に、ファイル転送の効率化も実現可能です。

目次
  1. ファイルアップロードの基礎
    1. 基本的なアップロード手順
  2. 再開機能の仕組み
    1. 再開機能の実装に必要な概念
    2. 再開機能のメリット
  3. 再開に必要なサーバーサイドの設定
    1. PHPの設定
    2. サーバーのタイムアウト設定
    3. 再開機能の動作確認
  4. 分割アップロードの実装
    1. 分割アップロードの仕組み
    2. 分割アップロードのメリット
    3. 分割アップロードの手順
  5. チャンク管理の手順
    1. チャンクの識別と管理方法
    2. チャンク管理の手順
    3. チャンク管理の注意点
  6. チャンクのアップロード処理
    1. クライアントサイドでのチャンクアップロード
    2. サーバーサイドでのチャンクアップロード処理
    3. アップロード完了後のファイル再構築
  7. チャンクアップロードの再開方法
    1. 再開時の進捗確認
    2. クライアントサイドでの再開処理
    3. サーバーサイドでのチャンク再開処理
    4. 再開機能のポイント
  8. 進捗の保存と確認方法
    1. 進捗の保存方法
    2. 進捗の更新
    3. 進捗の確認方法
    4. 進捗の保存と確認のポイント
  9. エラーハンドリングの重要性
    1. エラーハンドリングの主なケース
    2. PHPでのエラーハンドリング例
    3. クライアント側でのエラーハンドリング
    4. エラーハンドリングのポイント
  10. 再開機能の実装例
    1. サーバーサイドの実装
    2. クライアントサイドの実装
    3. アップロード完了時のファイル再構築
    4. 実装のポイント
  11. 応用例:大容量ファイルの再開アップロード
    1. 大容量ファイルアップロードの課題
    2. 大容量ファイルの再開アップロードにおける工夫
    3. サーバーサイドでのメモリ管理とタイムアウト設定
    4. 応用例の実装
    5. 大容量ファイル対応のまとめ
  12. まとめ

ファイルアップロードの基礎


PHPにおけるファイルアップロードは、主に$_FILESスーパーグローバル変数を利用して実現されます。HTMLフォームでenctype="multipart/form-data"を設定し、POSTリクエストでファイルをサーバーに送信することでアップロードが行われます。PHP側では、アップロードされたファイルを一時フォルダに保存し、必要に応じてサーバーの特定ディレクトリへ移動させます。

基本的なアップロード手順


ファイルアップロードの基本的な手順として、以下のポイントが挙げられます。

  1. ファイルの受信:フォームから送信されたファイルデータを$_FILESから取得します。
  2. サイズや拡張子のチェック:サーバーに保存する前にファイルのサイズや拡張子を確認し、不正なファイルを排除します。
  3. ファイルの移動:問題がなければ、move_uploaded_file関数を使って指定ディレクトリにファイルを移動します。

こうした基本的な処理に加え、再開機能を実装するために、後述するチャンク方式や進捗管理などの仕組みが必要になります。

再開機能の仕組み


ファイルアップロードの再開機能は、インターネット接続の中断やクライアント側のエラーなどで途中で中断されたアップロードを、続きから再開できるようにする仕組みです。これを実現するには、ファイル全体を一度にアップロードするのではなく、ファイルを小さなデータのかたまり(チャンク)に分割して順次アップロードする方法が一般的です。

再開機能の実装に必要な概念


再開機能を実装するためには、以下のポイントが重要です。

  1. チャンク方式:ファイルを複数の小さなチャンクに分け、それぞれをサーバーに順次送信する仕組みです。これにより、アップロードが中断された場合、完了していないチャンクから再開できます。
  2. 進捗の管理:どのチャンクまでアップロードが完了したかをサーバーに保存し、再開時に続きからアップロードを再開できるようにします。一般的にはデータベースや一時ファイルにチャンクの進捗状況を記録します。
  3. ID管理:ユーザーやファイルごとに固有のIDを割り当て、中断したアップロードを正確に再開するために利用します。

再開機能のメリット


再開機能を持たせることで、大容量ファイルのアップロード時に特に以下のようなメリットが得られます。

  • ユーザー体験の向上:中断しても再アップロードの手間が省けるため、ユーザーがストレスなく利用できるようになります。
  • 通信の効率化:一度アップロードされたチャンクを再送する必要がなく、リソースが節約されます。

この仕組みを基盤に、次にサーバーサイドの設定や各種手順について解説していきます。

再開に必要なサーバーサイドの設定


ファイルアップロードの再開機能をPHPで実装するためには、サーバーサイドの設定を適切に行う必要があります。特に、PHPの設定やサーバーのタイムアウト設定が重要で、再開機能がスムーズに動作するために適切な調整が必要です。

PHPの設定


PHPで再開機能を実現する際に確認するべき設定は以下の通りです。

  1. upload_max_filesizepost_max_size:アップロードするファイルのサイズに応じて、この値を十分に大きく設定します。ファイルをチャンクに分割してアップロードする場合でも、ファイルサイズの上限に注意する必要があります。
  2. max_execution_timemax_input_time:アップロードプロセスが中断されないように、スクリプトの実行時間や入力データの最大時間を適切に設定します。大容量ファイルのアップロードでは特に重要です。
  3. memory_limit:ファイルのチャンクをメモリに一時保存するため、必要なメモリサイズを確保するよう設定します。

サーバーのタイムアウト設定


アップロード処理が中断されず、再開機能が正常に動作するためには、サーバー側のタイムアウト設定も考慮する必要があります。

  • Webサーバーのタイムアウト設定:ApacheやNGINXなどのWebサーバーの設定で、タイムアウト値を長めに設定します。たとえば、NGINXではclient_body_timeoutproxy_read_timeoutを、ApacheではTimeoutディレクティブを調整します。
  • セッションの維持:アップロード進捗を管理するために、セッションを利用する場合があります。その際は、セッションタイムアウトの設定も適切に行います。

再開機能の動作確認


設定後、実際にファイルアップロードが再開できるかテストすることが重要です。サーバーのログやデバッグツールを使い、設定が反映されているか、アップロードプロセスに問題がないか確認します。

分割アップロードの実装


ファイルアップロードの再開機能を実現するためには、ファイルをチャンク(小さなデータ単位)に分割して順次アップロードする方法が一般的です。これにより、通信が中断されても、再度アップロードを開始する際に中断した箇所から続行できるようになります。

分割アップロードの仕組み


分割アップロードでは、ファイルを一定のサイズごとに小さなチャンクに分割し、個別にサーバーに送信します。サーバーは受け取ったチャンクを順次保存し、すべてのチャンクがアップロードされると、ファイルが完成します。

  • クライアントサイドでの分割:JavaScriptを利用して、クライアント側でファイルを複数のチャンクに分割します。
  • サーバーサイドでの再構築:サーバーは、受け取ったチャンクを元のファイルとして再構築する処理を行います。

分割アップロードのメリット


分割アップロードには以下のメリットがあります。

  • 効率的な再開:通信が中断した場合でも、既にアップロード済みのチャンクはそのまま保存されるため、未完了のチャンクから再開できます。
  • リソース管理の最適化:大容量ファイルを小さなチャンクに分割することで、サーバーの負荷を抑えながらアップロードを進められます。

分割アップロードの手順

  1. クライアントサイドでの分割処理:JavaScriptでファイルを指定のチャンクサイズに分割し、それぞれを順次サーバーに送信します。
  2. チャンク送信時のメタデータ付与:各チャンクには、ファイルの識別IDやチャンク番号などのメタデータを付与し、サーバー側で進捗管理ができるようにします。
  3. サーバーサイドでの受信と再構築:PHPスクリプトが各チャンクを受信し、進捗をデータベースや一時ファイルで記録し、すべてのチャンクが受信された時点でファイルを再構築します。

この分割アップロードの実装が、次のチャンク管理や再開機能の実装の基礎となります。

チャンク管理の手順


分割アップロードの実装において、チャンクごとにファイルの進捗を管理することは再開機能の要です。ここでは、各チャンクの状態を正確に記録し、途中から再開できるようにする手順について解説します。

チャンクの識別と管理方法


チャンク管理のためには、アップロードされた各チャンクがどのファイルに属するか、どの位置のチャンクかを識別する必要があります。以下のような情報を用いて管理します。

  1. ファイル識別ID:アップロードするファイルごとにユニークな識別IDを割り当てます。これは、ユーザーごとのアップロード進捗を区別するために重要です。
  2. チャンク番号:各チャンクに一連の番号を付け、アップロードの順序を管理します。これにより、どのチャンクがまだ未完了かを簡単に判別できます。
  3. アップロード状態の記録:サーバーサイドでデータベースや一時ファイルを使い、受信済みのチャンク番号を記録します。これにより、中断後も未受信のチャンクから再開できます。

チャンク管理の手順


チャンク管理を実現する手順は以下の通りです。

  1. チャンク送信時のメタデータ追加:各チャンク送信時に、ファイル識別IDとチャンク番号の情報を含めてサーバーに送信します。
  2. サーバー側での進捗確認:PHPスクリプトがチャンク番号を確認し、データベースまたは一時ファイルに進捗を記録します。
  3. 再開時の進捗確認:再開要求がある場合、サーバーは記録されたチャンクの進捗情報を参照し、未アップロードのチャンクのみを受け入れるようにします。

チャンク管理の注意点

  • 進捗情報の保持方法:一時ファイルやデータベースを利用して、進捗情報が正確に記録・維持されるようにします。
  • リソース管理:チャンクごとに一時ファイルやデータベースアクセスが増加するため、リソース負荷を軽減する仕組みが重要です。

こうしたチャンク管理によって、アップロードの途中から再開しやすくなり、ユーザーが効率的に大容量ファイルをアップロードできるようになります。

チャンクのアップロード処理


ファイルの分割アップロードでは、各チャンクを個別にサーバーへ送信する処理が必要です。この際、各チャンクのデータにファイルIDやチャンク番号を付与し、サーバーがチャンクごとの進捗を把握できるようにします。ここでは、チャンクをアップロードするためのPHPコード例とその実装手順について詳しく解説します。

クライアントサイドでのチャンクアップロード


クライアント側でJavaScriptを使用し、ファイルを指定したサイズのチャンクに分割して、サーバーへ順次送信します。以下は、JavaScriptの簡単なサンプルです。

function uploadChunk(file, chunkSize) {
    let start = 0;
    let chunkIndex = 0;
    const fileID = generateUniqueID(file); // ファイル識別IDを生成

    while (start < file.size) {
        const chunk = file.slice(start, start + chunkSize);
        const formData = new FormData();
        formData.append("fileID", fileID);
        formData.append("chunkIndex", chunkIndex);
        formData.append("fileChunk", chunk);

        fetch("/upload_chunk.php", {
            method: "POST",
            body: formData
        });

        start += chunkSize;
        chunkIndex++;
    }
}

このコードでは、ファイルをchunkSizeで指定されたサイズに分割し、各チャンクを/upload_chunk.phpに送信します。また、ファイルの識別IDとチャンク番号をFormDataに追加して、サーバー側で進捗が確認できるようにしています。

サーバーサイドでのチャンクアップロード処理


PHP側では、各チャンクを受信し、データベースや一時ファイルに進捗を記録しつつ最終的にファイルを再構築します。

<?php
$fileID = $_POST['fileID'];
$chunkIndex = $_POST['chunkIndex'];
$fileChunk = $_FILES['fileChunk']['tmp_name'];

// チャンクの一時保存場所を決定
$chunkDir = "uploads/{$fileID}/";
if (!is_dir($chunkDir)) {
    mkdir($chunkDir, 0777, true);
}
$chunkFile = $chunkDir . "chunk_{$chunkIndex}";

// チャンクを保存
move_uploaded_file($fileChunk, $chunkFile);

// 進捗をデータベースやファイルで管理(例: データベースで進捗管理)
updateUploadProgress($fileID, $chunkIndex);
?>

このPHPコードでは、各チャンクを一時ディレクトリ内に保存し、updateUploadProgress関数で進捗を記録します。こうすることで、アップロードの途中で中断しても、再開時に保存済みのチャンクから続きのチャンクを送信することができます。

アップロード完了後のファイル再構築


すべてのチャンクがアップロードされ、進捗が完了したと確認されたら、サーバー側でチャンクを結合し、最終的なファイルとして保存します。この再構築処理は次のステップで詳細に解説します。

チャンクアップロードの再開方法


ファイルアップロードが中断した場合でも、再開できるようにするための処理を実装します。再開機能を実現するためには、どのチャンクまでアップロード済みかをサーバーが把握しており、中断後も未アップロードのチャンクから再開できる仕組みが必要です。

再開時の進捗確認


再開の際には、クライアントが再度アップロードを開始する前に、サーバーへリクエストを送り、どのチャンクまで完了しているかを確認します。PHPでは、データベースや一時ファイルに保存した進捗情報を確認して、必要なチャンクだけをアップロードする指示を返します。

<?php
$fileID = $_POST['fileID'];
$lastUploadedChunk = getLastUploadedChunk($fileID); // 最新のチャンク番号を取得

echo json_encode(['lastUploadedChunk' => $lastUploadedChunk]);
?>

このコードは、getLastUploadedChunk関数で指定ファイルの最新チャンク番号を取得し、JSON形式でクライアントへ返します。クライアントは、この番号をもとに未アップロードのチャンクから再開します。

クライアントサイドでの再開処理


クライアント側で再開時にサーバーから取得した最新チャンク番号をもとに、未完了のチャンクから順次アップロードを再開します。

function resumeUpload(file, chunkSize, lastUploadedChunk) {
    let start = lastUploadedChunk * chunkSize;
    let chunkIndex = lastUploadedChunk + 1;
    const fileID = generateUniqueID(file);

    while (start < file.size) {
        const chunk = file.slice(start, start + chunkSize);
        const formData = new FormData();
        formData.append("fileID", fileID);
        formData.append("chunkIndex", chunkIndex);
        formData.append("fileChunk", chunk);

        fetch("/upload_chunk.php", {
            method: "POST",
            body: formData
        });

        start += chunkSize;
        chunkIndex++;
    }
}

このJavaScript関数では、lastUploadedChunkから再開するように設定し、アップロード済みのチャンクはスキップします。resumeUpload関数が呼ばれることで、中断した箇所から効率よく再開できるようになります。

サーバーサイドでのチャンク再開処理


サーバーでは、未完了のチャンクのみを受け取り、受信済みのチャンクはスキップするようにします。再開時も、進捗を継続的にデータベースや一時ファイルに記録し、再度中断しても再開できるようにします。

再開機能のポイント


再開機能の実装によって、大容量ファイルやネットワークの不安定な環境でも効率的にアップロードを続行できるようになり、ユーザー体験が向上します。また、進捗管理をしっかり行うことで、再開機能がより確実に動作します。

進捗の保存と確認方法


ファイルアップロードの再開機能を実現するには、アップロードの進捗状況をサーバー側に保存し、再開時にどこまで完了しているかを正確に確認できる仕組みが必要です。ここでは、進捗情報の保存方法と、再開の際の確認手順について説明します。

進捗の保存方法


進捗を保存するには、データベースや一時ファイルにチャンクごとのアップロード状況を記録します。データベースを利用する場合は、以下のような進捗情報を管理するテーブルを作成すると効果的です。

CREATE TABLE upload_progress (
    file_id VARCHAR(255) PRIMARY KEY,
    last_uploaded_chunk INT NOT NULL,
    total_chunks INT NOT NULL,
    upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

このテーブルでは、file_id(ファイルの識別ID)ごとにlast_uploaded_chunk(最新のチャンク番号)とtotal_chunks(全体のチャンク数)を記録します。

進捗の更新


各チャンクがアップロードされるたびに、サーバー側で進捗情報を更新します。これにより、アップロードが中断されても、再開時に最新の進捗が反映されます。

<?php
function updateUploadProgress($fileID, $chunkIndex) {
    // 進捗情報の更新
    $db = new PDO('mysql:host=localhost;dbname=mydatabase', 'user', 'password');
    $stmt = $db->prepare("UPDATE upload_progress SET last_uploaded_chunk = :chunkIndex WHERE file_id = :fileID");
    $stmt->execute([':chunkIndex' => $chunkIndex, ':fileID' => $fileID]);
}
?>

このPHP関数は、各チャンクがアップロードされるたびに、last_uploaded_chunkの値を更新することで、最新の進捗を記録します。

進捗の確認方法


再開時には、クライアントがどのチャンクまで完了しているかをサーバーに問い合わせ、再開位置を確認します。

<?php
function getUploadProgress($fileID) {
    $db = new PDO('mysql:host=localhost;dbname=mydatabase', 'user', 'password');
    $stmt = $db->prepare("SELECT last_uploaded_chunk FROM upload_progress WHERE file_id = :fileID");
    $stmt->execute([':fileID' => $fileID]);
    return $stmt->fetchColumn();
}
?>

この関数を用いて、再開時にサーバーから最新のlast_uploaded_chunkを取得し、クライアント側で未完了のチャンクから再開するようにします。

進捗の保存と確認のポイント

  • データベース vs 一時ファイル:データベースで進捗を管理するとデータの一貫性が保たれ、信頼性が向上します。一時ファイルを利用する方法もありますが、セッション管理が必要です。
  • 信頼性の確保:進捗情報が失われないように定期的にバックアップを取るか、アップロード完了後に不要なデータを削除するなどして、データの信頼性を保ちます。

進捗を保存しておくことで、中断されたアップロードもスムーズに再開でき、ユーザーにとって利便性の高い機能が提供可能です。

エラーハンドリングの重要性


ファイルアップロード機能において、エラーハンドリングは非常に重要です。アップロード中に発生する様々なエラーに対応することで、プロセスの安定性が向上し、ユーザー体験が向上します。特に分割アップロードや再開機能を実装する場合、途中でのエラー発生が大きな影響を与える可能性があるため、各エラーケースを想定した対策が必要です。

エラーハンドリングの主なケース


再開機能のあるファイルアップロードで考慮すべきエラーは以下の通りです。

  1. ネットワーク接続の中断:アップロード中にネットワーク接続が切れた場合、アップロードが中断されるため、クライアントは自動的に再試行できるように設計します。
  2. サーバーエラー:サーバーが予期せぬエラーで応答しない場合、エラーメッセージを表示して、アップロードを中断します。再試行可能な場合は、一定の時間後にリトライする設計を取り入れます。
  3. チャンクの不整合:チャンクが順番にアップロードされなかった場合や、サイズが異なる場合、クライアントは該当チャンクを再送信し、サーバー側もデータを上書きしないように注意します。
  4. ファイルサイズや形式の不適合:アップロードするファイルがサーバーで許可されたサイズや形式でない場合、ユーザーにエラーメッセージを表示し、送信を取りやめます。

PHPでのエラーハンドリング例


PHPでは、各エラーケースごとに適切なメッセージを返し、クライアント側で処理を行えるようにします。

<?php
try {
    // ファイルアップロード処理
    if ($_FILES['fileChunk']['error'] !== UPLOAD_ERR_OK) {
        throw new Exception("ファイルのアップロードに失敗しました");
    }

    // チャンクのサイズチェック
    $fileChunk = $_FILES['fileChunk']['tmp_name'];
    if (filesize($fileChunk) > MAX_CHUNK_SIZE) {
        throw new Exception("チャンクサイズが大きすぎます");
    }

    // チャンクの保存
    move_uploaded_file($fileChunk, $chunkFile);
} catch (Exception $e) {
    echo json_encode(['error' => $e->getMessage()]);
}
?>

この例では、エラーが発生した際に適切なエラーメッセージをJSON形式でクライアントへ返し、再試行やエラー表示を容易にしています。

クライアント側でのエラーハンドリング


クライアントサイドでもエラーメッセージを受け取って適切な対応をします。JavaScriptでエラーメッセージを受け取り、表示する例を示します。

fetch("/upload_chunk.php", { method: "POST", body: formData })
    .then(response => response.json())
    .then(data => {
        if (data.error) {
            console.error("Error:", data.error);
            // ユーザーにエラーを通知し、再試行オプションを提供
            alert(data.error);
        } else {
            console.log("Chunk uploaded successfully");
        }
    })
    .catch(error => console.error("Network error:", error));

このコードでは、サーバーからのエラーメッセージをユーザーに通知し、再試行を促します。

エラーハンドリングのポイント

  • ユーザーへのフィードバック:エラー発生時に、ユーザーが現在の状況を把握し、次に何をすべきかが分かるように詳細なメッセージを表示します。
  • 自動再試行:ネットワークエラーなどで一時的に接続が途切れた場合は、自動的に再試行する設計も有効です。

エラーハンドリングを強化することで、ファイルアップロードの信頼性が向上し、ユーザーにとって利便性の高い機能を提供できます。

再開機能の実装例


ここでは、PHPを使って中断されたファイルアップロードを再開できる具体的な実装例を紹介します。この例では、分割アップロード、進捗の記録と確認、エラーハンドリングの要素をすべて含み、再開機能を効率的に実現するための方法を示します。

サーバーサイドの実装


まず、PHPでチャンクごとのアップロードを処理し、進捗情報をデータベースに保存する処理を実装します。以下のコード例では、チャンクの保存、進捗記録、再開時の確認を行っています。

<?php
// 必要なデータをPOSTから取得
$fileID = $_POST['fileID'];
$chunkIndex = $_POST['chunkIndex'];
$totalChunks = $_POST['totalChunks'];
$fileChunk = $_FILES['fileChunk']['tmp_name'];

// データベース接続
$db = new PDO('mysql:host=localhost;dbname=mydatabase', 'user', 'password');

// ディレクトリの確認と作成
$uploadDir = "uploads/{$fileID}/";
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0777, true);
}

// チャンクの保存処理
$chunkFile = $uploadDir . "chunk_{$chunkIndex}";
if (move_uploaded_file($fileChunk, $chunkFile)) {
    // 進捗情報を更新
    $stmt = $db->prepare("INSERT INTO upload_progress (file_id, last_uploaded_chunk, total_chunks) 
                          VALUES (:fileID, :chunkIndex, :totalChunks)
                          ON DUPLICATE KEY UPDATE last_uploaded_chunk = :chunkIndex");
    $stmt->execute([':fileID' => $fileID, ':chunkIndex' => $chunkIndex, ':totalChunks' => $totalChunks]);

    echo json_encode(['status' => 'success']);
} else {
    echo json_encode(['status' => 'error', 'message' => 'ファイル保存に失敗しました']);
}
?>

このコードでは、各チャンクをサーバーに保存し、upload_progressテーブルにチャンクの進捗情報を記録します。また、すでにアップロード済みのチャンクがある場合は上書きされないようにし、重複したアップロードが避けられるようにしています。

クライアントサイドの実装


クライアント側でファイルを分割し、再開するためのJavaScriptコードを実装します。この例では、ファイルの進捗情報をサーバーに問い合わせ、未完了のチャンクからアップロードを再開します。

async function resumeUpload(file, chunkSize) {
    const fileID = generateUniqueID(file);

    // サーバーに最新のチャンク情報をリクエスト
    const response = await fetch("/get_upload_progress.php", {
        method: "POST",
        body: JSON.stringify({ fileID })
    });
    const { lastUploadedChunk } = await response.json();
    let startChunk = lastUploadedChunk + 1;

    for (let chunkIndex = startChunk; chunkIndex < Math.ceil(file.size / chunkSize); chunkIndex++) {
        const start = chunkIndex * chunkSize;
        const chunk = file.slice(start, start + chunkSize);

        const formData = new FormData();
        formData.append("fileID", fileID);
        formData.append("chunkIndex", chunkIndex);
        formData.append("totalChunks", Math.ceil(file.size / chunkSize));
        formData.append("fileChunk", chunk);

        await fetch("/upload_chunk.php", { method: "POST", body: formData });
    }
}

このコードでは、サーバーから最新の進捗情報を取得し、次の未アップロードチャンクからアップロードを再開します。すべてのチャンクが正常にアップロードされると、アップロード完了となります。

アップロード完了時のファイル再構築


サーバー側で全チャンクがアップロードされたと判断した後、個別のチャンクファイルを結合して元のファイルを再構築します。

<?php
$fileID = $_POST['fileID'];
$uploadDir = "uploads/{$fileID}/";
$finalFile = "uploads/{$fileID}.complete";

// 全チャンクを結合して最終ファイルを作成
$fp = fopen($finalFile, "wb");
for ($i = 0; $i < $totalChunks; $i++) {
    $chunkFile = $uploadDir . "chunk_{$i}";
    $chunkData = file_get_contents($chunkFile);
    fwrite($fp, $chunkData);
    unlink($chunkFile); // チャンクを削除
}
fclose($fp);

// 結合が完了したら一時ディレクトリを削除
rmdir($uploadDir);
?>

このコードで、サーバーはアップロードされたチャンクをすべて結合し、最終的なファイルを作成します。完了後、チャンクファイルは削除され、ストレージの無駄を防ぎます。

実装のポイント

  • データの整合性:アップロード中に発生するエラーやネットワークの中断を処理し、データが損失しないようにします。
  • 進捗の定期更新:進捗状況を適切に保存し、再開時に正確に最新の状態を反映させます。

この実装例により、中断したファイルアップロードが再開できるシステムを実現し、大容量ファイルでも効率的にアップロードを完了できるようになります。

応用例:大容量ファイルの再開アップロード


大容量ファイル(例えば数GB以上)のアップロードでは、途中での中断やリソース管理が特に重要です。ここでは、再開機能を大容量ファイルに応用する際の注意点と、よりスムーズな再開アップロードを実現するための工夫について解説します。

大容量ファイルアップロードの課題


大容量ファイルのアップロードには、以下のような課題が生じやすく、対応策が必要です。

  1. 通信の不安定性:ファイルが大きくなるほど、アップロードにかかる時間が長くなり、通信の中断リスクも高まります。再開機能により、中断しても途中から再開できる設計が不可欠です。
  2. サーバーリソースの管理:大容量データの連続アップロードは、サーバーに負担をかけます。各チャンクの処理で一時的なディスク容量やメモリ使用量が増えるため、適切なリソース管理が求められます。
  3. ファイルの一貫性:アップロード途中でチャンクが失われると、ファイル全体の整合性が損なわれます。進捗管理や整合性チェックが重要です。

大容量ファイルの再開アップロードにおける工夫


大容量ファイルに対する再開アップロードでは、いくつかの工夫を施すことで、安定したアップロード体験が可能になります。

  1. 小さなチャンクサイズの設定:チャンクサイズを小さくすることで、アップロードごとの負荷を軽減し、再送する際のデータ量も最小限に抑えられます。一般的に、2〜5MB程度のチャンクサイズが推奨されます。
  2. MD5やSHA-256ハッシュによる整合性確認:アップロード済みのチャンクが破損していないか確認するために、チャンクごとにハッシュ値を計算し、サーバー側で照合することが有効です。ハッシュが一致しない場合、そのチャンクだけ再送信を行います。
  3. データベースによる進捗管理の強化:大容量ファイルのアップロードでは、チャンクの進捗状況をデータベースで厳密に管理し、サーバーがどのチャンクまで受信済みかを正確に記録します。これにより、アップロードが途中で中断されても、未完了のチャンクのみを再送でき、効率が向上します。

サーバーサイドでのメモリ管理とタイムアウト設定


大容量ファイルのアップロードでは、サーバー設定も調整する必要があります。

  • メモリ使用量の制限memory_limitの設定を適切に行い、大容量ファイルの分割アップロードでもメモリが過負荷にならないようにします。
  • タイムアウトの延長:アップロード完了まで十分な時間を確保するため、max_execution_timemax_input_timeを長めに設定することが推奨されます。

応用例の実装


大容量ファイルの再開アップロードの実装例を示します。ここでは、ハッシュ照合による整合性チェックを行い、チャンクごとに進捗を記録します。

<?php
$fileID = $_POST['fileID'];
$chunkIndex = $_POST['chunkIndex'];
$totalChunks = $_POST['totalChunks'];
$expectedHash = $_POST['hash']; // チャンクのハッシュ
$fileChunk = $_FILES['fileChunk']['tmp_name'];

// チャンクを保存
$chunkFile = "uploads/{$fileID}/chunk_{$chunkIndex}";
move_uploaded_file($fileChunk, $chunkFile);

// ハッシュ照合による整合性確認
$actualHash = hash_file('sha256', $chunkFile);
if ($actualHash !== $expectedHash) {
    echo json_encode(['status' => 'error', 'message' => 'ハッシュが一致しません']);
    unlink($chunkFile); // 不一致の場合、チャンクを削除
    exit;
}

// 進捗の更新
$db->prepare("UPDATE upload_progress SET last_uploaded_chunk = :chunkIndex WHERE file_id = :fileID")
    ->execute([':chunkIndex' => $chunkIndex, ':fileID' => $fileID]);

echo json_encode(['status' => 'success']);
?>

このコードは、クライアント側から送られたハッシュ値とサーバー側で計算したハッシュ値を照合し、整合性を確認します。ハッシュが一致すればチャンクを保存し、再開時にも信頼性が維持されます。

大容量ファイル対応のまとめ

  • 信頼性の向上:ハッシュ照合でデータの一貫性を確保し、不完全なデータを受け付けないようにします。
  • 負荷管理:小さなチャンクサイズを利用し、メモリとディスクの使用量を最適化します。

このように、大容量ファイルに対応した再開アップロード機能は、信頼性と効率性を向上させ、ユーザーにとって利便性の高いアップロード機能を提供します。

まとめ


本記事では、PHPでのファイルアップロード再開機能の実装方法を解説しました。再開機能の基本概念から、チャンクアップロードの仕組み、進捗管理、エラーハンドリング、さらに大容量ファイルへの応用例まで、細かな実装方法を紹介しました。

再開機能を備えることで、ユーザーはアップロードが中断してもスムーズに続きから再開でき、ストレスなく大容量ファイルもアップロードできます。信頼性を高め、通信の効率化を図ることで、より安定したファイルアップロード機能を実現可能です。

コメント

コメントする

目次
  1. ファイルアップロードの基礎
    1. 基本的なアップロード手順
  2. 再開機能の仕組み
    1. 再開機能の実装に必要な概念
    2. 再開機能のメリット
  3. 再開に必要なサーバーサイドの設定
    1. PHPの設定
    2. サーバーのタイムアウト設定
    3. 再開機能の動作確認
  4. 分割アップロードの実装
    1. 分割アップロードの仕組み
    2. 分割アップロードのメリット
    3. 分割アップロードの手順
  5. チャンク管理の手順
    1. チャンクの識別と管理方法
    2. チャンク管理の手順
    3. チャンク管理の注意点
  6. チャンクのアップロード処理
    1. クライアントサイドでのチャンクアップロード
    2. サーバーサイドでのチャンクアップロード処理
    3. アップロード完了後のファイル再構築
  7. チャンクアップロードの再開方法
    1. 再開時の進捗確認
    2. クライアントサイドでの再開処理
    3. サーバーサイドでのチャンク再開処理
    4. 再開機能のポイント
  8. 進捗の保存と確認方法
    1. 進捗の保存方法
    2. 進捗の更新
    3. 進捗の確認方法
    4. 進捗の保存と確認のポイント
  9. エラーハンドリングの重要性
    1. エラーハンドリングの主なケース
    2. PHPでのエラーハンドリング例
    3. クライアント側でのエラーハンドリング
    4. エラーハンドリングのポイント
  10. 再開機能の実装例
    1. サーバーサイドの実装
    2. クライアントサイドの実装
    3. アップロード完了時のファイル再構築
    4. 実装のポイント
  11. 応用例:大容量ファイルの再開アップロード
    1. 大容量ファイルアップロードの課題
    2. 大容量ファイルの再開アップロードにおける工夫
    3. サーバーサイドでのメモリ管理とタイムアウト設定
    4. 応用例の実装
    5. 大容量ファイル対応のまとめ
  12. まとめ