PHPでファイルのダウンロードを強制する方法


122

ユーザーがPHPでWebページにアクセスしたときにファイルのダウンロードを要求したいのですが。とは何か関係があると思いますがfile_get_contents、どうやって実行するのかわかりません。

$url = "http://example.com/go.exe";

ファイルをダウンロードした後、header(location)別のページにリダイレクトされていません。止まるだけです。


PHP

回答:


232

組み込みのPHP関数readfileに関するドキュメントを読む

$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary"); 
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\""); 
readfile($file_url); 

また、ファイルapplication / zip、application / pdfなどに基づいて適切なコンテンツタイプを追加してください。ただし、名前を付けて保存ダイアログをトリガーしたくない場合のみです。


6
なぜそれが必要なのですか?
ジョン

.Else以外の.exeファイル以外の場合、これは正常に機能するはずです。
ピットディガー2011

7
フラッシュすることを忘れないでください;-) ob_clean(); 流す(); / *前* / readfile($ file_url);
アッシュ

それで、10GBの大きなファイルがある場合、phpはそのファイル全体をロードしようとしますか?
GDY

潜在的な問題を回避するために、exit()は最後に呼び出す必要があります(経験から言えば:-)
ykayによると、Reinstate Monica

42
<?php
$file = "http://example.com/go.exe"; 

header("Content-Description: File Transfer"); 
header("Content-Type: application/octet-stream"); 
header("Content-Disposition: attachment; filename=\"". basename($file) ."\""); 

readfile ($file);
exit(); 
?>

または、ブラウザでファイルを開くことができない場合は、Locationヘッダーを使用できます。

<?php header("Location: http://example.com/go.exe"); ?>

1
ヘッダーのファイル名は$file(http部分を含む)ではなく、有効なファイル名にする必要があります。
Fabio

1
アプリケーション/強制ダウンロードメディアタイプはありません。使用アプリケーション/ octet-streamの代わりに。
ガンボ2011

とてもうまくいきます!しかし、保存されたファイル名に 'を追加します。使用してください:filename = "
。basename

@ harry4516はあなたの発見に応じて変更されました
Marek Sebera

私のためにPNG画像の場合、親愛なる動作しません。ありがとう。
Kamlesh

40
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\""); 
echo readfile($url);

正しい

以下のための1つまたはそれ以上のexeファイルの種類

header("Location: $url");

@ファビオ:より多くの詳細を追加します
創世記

とても簡単でした。出来た。そのときのfile-Get_contentsとは何ですか?ちょっと興味があるんだけど。ありがとう。
ジョン、2011

サーバーにダウンロードするには
創世記

10
戻り値は「ファイルから読み込まれたバイト数を返します。エラーが発生した場合はFALSEが返され、関数が@readfile()として呼び出されない限り、「readfile()」の前にある「echo」を削除するだけです。エラーメッセージが出力されます。」したがって、ファイルのコンテンツと、コンテンツの最後にある整数が返されます。
Mladen B. 2013

22

最初にファイルを表示し、その値をurlに設定します。

index.php

<a href="download.php?download='.$row['file'].'" title="Download File">

download.php

<?php
/*db connectors*/
include('dbconfig.php');

/*function to set your files*/
function output_file($file, $name, $mime_type='')
{
    if(!is_readable($file)) die('File not found or inaccessible!');
    $size = filesize($file);
    $name = rawurldecode($name);
    $known_mime_types=array(
        "htm" => "text/html",
        "exe" => "application/octet-stream",
        "zip" => "application/zip",
        "doc" => "application/msword",
        "jpg" => "image/jpg",
        "php" => "text/plain",
        "xls" => "application/vnd.ms-excel",
        "ppt" => "application/vnd.ms-powerpoint",
        "gif" => "image/gif",
        "pdf" => "application/pdf",
        "txt" => "text/plain",
        "html"=> "text/html",
        "png" => "image/png",
        "jpeg"=> "image/jpg"
    );

    if($mime_type==''){
        $file_extension = strtolower(substr(strrchr($file,"."),1));
        if(array_key_exists($file_extension, $known_mime_types)){
            $mime_type=$known_mime_types[$file_extension];
        } else {
            $mime_type="application/force-download";
        };
    };
    @ob_end_clean();
    if(ini_get('zlib.output_compression'))
    ini_set('zlib.output_compression', 'Off');
    header('Content-Type: ' . $mime_type);
    header('Content-Disposition: attachment; filename="'.$name.'"');
    header("Content-Transfer-Encoding: binary");
    header('Accept-Ranges: bytes');

    if(isset($_SERVER['HTTP_RANGE']))
    {
        list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
        list($range) = explode(",",$range,2);
        list($range, $range_end) = explode("-", $range);
        $range=intval($range);
        if(!$range_end) {
            $range_end=$size-1;
        } else {
            $range_end=intval($range_end);
        }

        $new_length = $range_end-$range+1;
        header("HTTP/1.1 206 Partial Content");
        header("Content-Length: $new_length");
        header("Content-Range: bytes $range-$range_end/$size");
    } else {
        $new_length=$size;
        header("Content-Length: ".$size);
    }

    $chunksize = 1*(1024*1024);
    $bytes_send = 0;
    if ($file = fopen($file, 'r'))
    {
        if(isset($_SERVER['HTTP_RANGE']))
        fseek($file, $range);

        while(!feof($file) &&
            (!connection_aborted()) &&
            ($bytes_send<$new_length)
        )
        {
            $buffer = fread($file, $chunksize);
            echo($buffer);
            flush();
            $bytes_send += strlen($buffer);
        }
        fclose($file);
    } else
        die('Error - can not open file.');
    die();
}
set_time_limit(0);

/*set your folder*/
$file_path='uploads/'."your file";

/*output must be folder/yourfile*/

output_file($file_path, ''."your file".'', $row['type']);

/*back to index.php while downloading*/
header('Location:index.php');
?>

2
どこから手に入れたの?それは強力なようです
アクセルA.ガルシア

@ jundell-agboは興味深いもので、CRTファイルについてですか?
fralbo 2016年

これは非常に大きなファイルでも機能します。素晴らしいソリューション。しかし、 `$ chunksize = 1 *(1024 * 1024);`は低速です。複数の値を試してみたところ、 `$ chunksize = 8 *(1024 * 1024);`が使用可能なすべての帯域幅を使用していることに気付きました。
Linga

16

エラーのmemory_limit原因となる、許可されたメモリ制限(ini設定)より大きいサイズのファイルをダウンロードする必要がある場合は、次のようにしますPHP Fatal error: Allowed memory size of 5242880 bytes exhausted

// File to download.
$file = '/path/to/file';

// Maximum size of chunks (in bytes).
$maxRead = 1 * 1024 * 1024; // 1MB

// Give a nice name to your download.
$fileName = 'download_file.txt';

// Open a file in read mode.
$fh = fopen($file, 'r');

// These headers will force download on browser,
// and set the custom file name for the download, respectively.
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');

// Run this until we have read the whole file.
// feof (eof means "end of file") returns `true` when the handler
// has reached the end of file.
while (!feof($fh)) {
    // Read and output the next chunk.
    echo fread($fh, $maxRead);

    // Flush the output buffer to free memory.
    ob_flush();
}

// Exit to make sure not to output anything else.
exit;

私はこれを試したところ、サーバーが停止しました。ログに大量のエラーを書き込みました。
ダニエルウィリアムズ

ログの内容を知っておくと便利です。
Parziphal 2017

申し訳ありませんが、ログファイルが非常に大きくなったため、それを変更する必要があったため、表示されませんでした。
ダニエルウィリアムズ

アクセスが非常に制限されていたサーバーに、このようなものをセットアップする必要がありました。ダウンロードスクリプトがホームページにリダイレクトされ続け、その理由がわかりませんでした。今私は問題がメモリエラーであり、このコードがそれを解決したことを知っています。
ギャビン

使用するだけの場合ob_flush()、エラーが発生する可能性があります:ob_flush():バッファのフラッシュに失敗しました。フラッシュするバッファがありません。それをif (ob_get_level() > 0) {ob_flush();}(Reference stackoverflow.com/a/9182133/128761)で囲みます
vee

11

実行時にMIMEタイプも検出する、上記の受け入れられた回答の変更:

$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $path));

$finfo = finfo_open(FILEINFO_MIME_ENCODING);
header('Content-Transfer-Encoding: '.finfo_file($finfo, $path)); 

header('Content-disposition: attachment; filename="'.basename($path).'"'); 
readfile($path); // do the double-download-dance (dirty but worky)

4

次のコードは、次のチュートリアルで説明するように、ダウンロードサービスをPHPに実装する正しい方法です。

header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = @fopen($filePath, "rb");
while(!feof($file)) {
    print(@fread($file, 1024*8));
    ob_flush();
    flush();
}

1
このコードにはfile_name、およびの2つの変数がありfilePathます。あまり良いコーディング方法ではありません。特に説明なし。外部チュートリアルへのリンクは役に立ちません。
Khom Nazid

2

これを試して:

header('Content-type: audio/mp3'); 
header('Content-disposition: attachment; 
filename=“'.$trackname'”');                             
readfile('folder name /'.$trackname);          
exit();

2

http://php.net/manual/en/function.readfile.php

それだけで十分です。「Monkey.gif」をファイル名に変更します。他のサーバーからダウンロードする必要がある場合は、「monkey.gif」を「http://www.exsample.com/go.exe」に変更します


1
StackOverflowへようこそ。リンクの問題を回避するために、リンクを追加するのではなく、コードを直接含めることを常にお勧めします。
ストレンジャー

1

ダウンロードをストリーミングすることもできます。これにより、消費するリソースが大幅に減少します。例:

$readableStream = fopen('test.zip', 'rb');
$writableStream = fopen('php://output', 'wb');

header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="test.zip"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();

上記の例では、test.zipをダウンロードしています(実際には、ローカルマシンのandroid studio zipでした)。php:// outputは書き込み専用ストリームです(通常、echoまたはprintで使用されます)。その後、必要なヘッダーを設定し、stream_copy_to_stream(source、destination)を呼び出すだけです。stream_copy_to_stream()メソッドは、ソースストリーム(読み取りストリーム)から入力を受け取り、それを宛先ストリーム(書き込みストリーム)にパイプするパイプとして機能し、許可されたメモリが使い果たされるという問題を回避して、実際に大きなファイルをダウンロードできるようにしますあなたのPHPよりmemory_limit


ダウンロードがストリーミングされるとはどういう意味ですか?
パルサヤズダニ

1
@ParsaYazdaniダウンロードされたデータはチャンク化されます。詳細については、ストリーミングの概念をご覧ください。注:PHPでファイルをストリーミング(チャンク)ダウンロードするのはこれだけではありません。しかし、それは確かに最も簡単な方法の1つです。
サウドクレシ

0

私の上の答えはうまくいきます。しかし、私はGETを使用してそれを実行する方法の方法を提供したいと思います

あなたのhtml / phpページ上

$File = 'some/dir/file.jpg';
<a href="<?php echo '../sumdir/download.php?f='.$File; ?>" target="_blank">Download</a>

download.phpが含まれています

$file = $_GET['f']; 

header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);

header("Content-type: application/".$ext);
header('Content-length: '.filesize($file));
header("Content-Disposition: attachment; filename=\"$basename\"");
ob_clean(); 
flush();
readfile($file);
exit;

これはどのファイルタイプでも機能するはずです。これはPOSTを使用してテストされていませんが、機能する可能性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.