PHP - ファイルのダウンロード

こんにちは、PHPの志願者たち!今日は、あなたのウェブサイトに素晴らしい機能を追加するためのエキサイティングな話題に取り組んでみましょう - PHPを使ったファイルのダウンロードです。あなたの近所の親切なコンピュータの先生として、私はこの旅をステップバイステップで案内します。お気に入りの飲み物を用意して、リラックスして、コーディングを始めましょう!

PHP - Download File

readfile() 関数

PHPでのファイルダウンロードの中心には、readfile() 関数があります。この便利な小さな関数は、ファイルを読み取り、その内容を出力バッファーに書き込むための優れたツールです。もしこれが少し技術的な感じがするなら、一緒に分解してみましょう!

基本的な使い方

簡単な例から始めましょう:

<?php
readfile("example.txt");
?>

このコードスニペットでは、readfile() が "example.txt" の内容を読み取り、直接ブラウザに表示します。まるで魔法のようです - ファイルの内容が画面に表示されます!

ファイルのダウンロード

さて、ここが面白いところです。readfile() を使ってファイルのダウンロードを促すことができます。もっと完全な例を見てみましょう:

<?php
$file = "myfile.pdf";

if(file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
?>

これは相当な量の情報です!分解してみましょう:

  1. ファイルをダウンロードするファイルを指定します($file = "myfile.pdf")。
  2. ファイルが存在するかどうかを file_exists() で確認します。
  3. ファイルが存在する場合、以下のヘッダーを設定します:
  • Content-Description はブラウザにファイル転送であることを伝えます。
  • Content-Type は一般的なバイナリファイルの MIME タイプを設定します。
  • Content-Disposition はブラウザにファイルのダウンロードを促します。
  • 他のヘッダーはファイルがキャッシュされないようにし、適切に処理されるようにします。
  1. 最後に、readfile() を使ってファイルの内容を出力します。

一般的なファイルタイプ

異なるファイルタイプには異なるヘッダーが必要です。以下は一般的なファイルタイプとそれに対応する Content-Type ヘッダーの表です:

ファイルタイプ Content-Type ヘッダー
PDF application/pdf
ZIP application/zip
JPEG image/jpeg
PNG image/png
MP3 audio/mpeg
MP4 video/mp4

ダウンロードリンクの作成

ダウンロードを促す方法を知ったので、ユーザーフレンドリーなダウンロードリンクを作成してみましょう:

<!DOCTYPE html>
<html>
<body>

<h2>ファイルのダウンロード</h2>
<p><a href="download.php?file=myfile.pdf">PDFをダウンロード</a></p>

</body>
</html>

この HTML では、ファイルのダウンロードを処理する PHP スクリプト(download.php)へのリンクを作成しています。ファイル名を URL のパラメータとして渡しています。

さて、download.php スクリプトを作成してみましょう:

<?php
if(isset($_GET['file'])) {
$file = $_GET['file'];
$filepath = "uploads/" . $file;

if(file_exists($filepath)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($filepath).'"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
exit;
} else {
echo "ファイルが見つかりません。";
}
}
?>

このスクリプトは以下を行います:

  1. URLにファイルパラメータが渡されているか確認します。
  2. ファイルのフルパスを構築します(ファイルが "uploads" ディレクトリにあると仮定します)。
  3. ファイルが存在する場合、ヘッダーを設定し、ダウンロードを促します。
  4. ファイルが存在しない場合、エラーメッセージを表示します。

セキュリティの考慮

さて、あなたは何を考えていますか?「でも先生、これは少し危険じゃないですか?誰かが許可されていないファイルをダウンロードしようとするたらどうなるんですか?」素晴らしい質問です!あなたの考えは正しいです。それで、私たちはセキュリティについて話し合う必要があります。

ファイルパスの検証

ユーザーの入力を直接信頼すべきではありません。以下はダウンロードスクリプトの改善版です:

<?php
if(isset($_GET['file'])) {
$file = basename($_GET['file']);
$filepath = "uploads/" . $file;

// ファイルパスを検証
$realPath = realpath($filepath);
$uploadDir = realpath("uploads/");

if($realPath === false || strpos($realPath, $uploadDir) !== 0) {
die("無効なファイルパス。");
}

if(file_exists($filepath)) {
// ... (ダウンロードコードの残り)
} else {
echo "ファイルが見つかりません。";
}
}
?>

このバージョンでは:

  1. basename() を使ってディレクトリの traversal 攻撃を防ぎます。
  2. realpath() を使ってファイルとアップロードディレクトリの実際のパスを取得します。
  3. ファイルの実際のパスがアップロードディレクトリパスで始まるか確認します。

結論

そして、皆さん!私たちは PHP ファイルダウンロードの土地を旅しました。readfile() の基本からダウンロードリンクの作成まで、そしてセキュリティの重要な考慮まで触れました。力があれば責任も伴います - ユーザーの入力を検証し、ファイルダウンロードを行う際にはセキュリティを考えましょう。

このレッスンを楽しんでいただけたことを願っています。続けて練習し、コーディングを続けると、すぐに素晴らしい PHP アプリケーションを作成し、さまざまなダウンロード機能を追加できるようになります。次回まで、ハッピーコーディング!

Credits: Image by storyset