URLからサーバーにファイルをダウンロード


341

ええと、これはかなりシンプルに見えます。サーバーにファイルをダウンロードするために必要なことは次のとおりです。

file_put_contents("Tmpfile.zip", file_get_contents("http://someurl/file.zip"));

問題は1つだけです。100MBのような大きなファイルがある場合はどうでしょう。次に、メモリが不足し、ファイルをダウンロードできなくなります。

私が欲しいのは、ファイルをダウンロードしながらディスクに書き込む方法です。そうすれば、メモリの問題にぶつかることなく、大きなファイルをダウンロードできます。


4
それはあなたのサーバー設定で設定されています、PHPは私が知る限り実際にはそれを回避できません(直接.ini編集を除く)
Ben

回答:


494

PHP 5.1.0以降file_put_contents()、ストリームハンドルを$dataパラメータとして渡すことで、ピースごとの書き込みをサポートしています。

file_put_contents("Tmpfile.zip", fopen("http://someurl/file.zip", 'r'));

マニュアルから:

場合データ [二番目の引数である]ストリームリソースであり、そのストリームの残りのバッファが指定したファイルにコピーされます。これはを使用する場合と同様 stream_copy_to_stream()です。

Hakreに感謝します。)


4
それは私の最初の選択ではありません。allow_fopen_url Offがphp.iniで設定されている場合(セキュリティの観点からは良い考え)、スクリプトが破損します。
PleaseStand

4
@idealmachine file_get_contents()それが事実である場合、私はどちらも機能しないと思います(OPを参照)。
アレックス

10
@geoff私は具体的でした、あなたが望んだ機能に言及しました。あなたが欲しかったかもしれないのは、誰かがあなたのためにコードを書いた人でした-しかし、私はあなたがそれを自分で行うことを学んだと確信しています。また、お互いのSOの相互作用についてコメントする場合は、さらにいくつかの回答を受け入れてください :)
アレックス

@alex:ぜひ編集内容をご覧ください。このコメントをいつ削除できるかをお知らせください。
2013年

4
'b'フラグは、ほとんどの場合で使用する必要がありますfopen。画像やその他の非プレーンテキストファイルへの悪影響を防ぎます。
ウェインウェイベル2013

132
private function downloadFile($url, $path)
{
    $newfname = $path;
    $file = fopen ($url, 'rb');
    if ($file) {
        $newf = fopen ($newfname, 'wb');
        if ($newf) {
            while(!feof($file)) {
                fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
            }
        }
    }
    if ($file) {
        fclose($file);
    }
    if ($newf) {
        fclose($newf);
    }
}

1
あなたの抜粋に感謝しますが、あなたのコード@xaavを説明できますか?私はphpが得意ではありません。1024 * 8とは何ですか?再度、感謝します。
vvMINOvv

@wMINOw行の長さ。
DavidBélanger12年

2
具体的には、パラメータがバイト単位であるため、一度に最大8KB(KBあたり1024バイト* 8)を読み取ることを意味します。行が8KB以下である限り、行全体を一度に読み取ります。
Doktor J

1
なぜこれが最善の答えではないのですか?
GunJack

1
このアプローチでエラーをどのように処理しますか?404が返された場合、または接続が中断された場合、またはタイムアウトになった場合はどうなりますか?
Adam Swinden 14

67

cURLを使用してみてください

set_time_limit(0); // unlimited max execution time
$options = array(
  CURLOPT_FILE    => '/path/to/download/the/file/to.zip',
  CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
  CURLOPT_URL     => 'http://remoteserver.com/path/to/big/file.zip',
);

$ch = curl_init();
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);

確かではありませんがCURLOPT_FILE、データをプルするときに書き込むオプションを使用すると信じています。バッファリングされていません。


2
通常、これで問題ありませんが、私はこのコードをWebアプリに持っているので、ユーザーがcURLをインストールするかどうか確信が持てません。しかし、私はこれに投票をしました。
xaav

@Geoffは分散Webアプリですか?ホスティングを制御する場合は、ユーザーについては関係ありません(cURLはサーバー上のライブラリです)。
アレックス

いいえ、ホスティングを管理していません。これは、誰でも持つことができる分散Webアプリです。
xaav

3
カールがない可能性があります。しかし、ほとんどすべての共有ホスティング会社には、デフォルトでCURLがインストールされています。つまり、そうでないものを見たことがありません。
Mangirdas Skripka

19
私のテストのように、CURLOPT_FILEにファイルパスを直接割り当てることはできません。ファイルハンドラでなければなりません。まず、使用してファイル開く$fh = fopen('/path/to/download/the/file/to.zip', 'w');と、クローズfclose($fh);後にcurl_close($ch);。そして設定CURLOPT_FILE => $fh
グスタボ

22

プロデジタルソンの答えは私にはうまくいきませんでした。私が得たmissing fopen in CURLOPT_FILE 詳細を

これは私のために、ローカルURLを含めて機能しました:

function downloadUrlToFile($url, $outFileName)
{   
    if(is_file($url)) {
        copy($url, $outFileName); 
    } else {
        $options = array(
          CURLOPT_FILE    => fopen($outFileName, 'w'),
          CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
          CURLOPT_URL     => $url
        );

        $ch = curl_init();
        curl_setopt_array($ch, $options);
        curl_exec($ch);
        curl_close($ch);
    }
}

19
  1. 宛先サーバーに「downloads」というフォルダを作成します
  2. [このコード]を.phpファイルに保存し、宛先サーバーで実行します

ダウンローダー:

<html>
<form method="post">
<input name="url" size="50" />
<input name="submit" type="submit" />
</form>
<?php
    // maximum execution time in seconds
    set_time_limit (24 * 60 * 60);

    if (!isset($_POST['submit'])) die();

    // folder to save downloaded files to. must end with slash
    $destination_folder = 'downloads/';

    $url = $_POST['url'];
    $newfname = $destination_folder . basename($url);

    $file = fopen ($url, "rb");
    if ($file) {
      $newf = fopen ($newfname, "wb");

      if ($newf)
      while(!feof($file)) {
        fwrite($newf, fread($file, 1024 * 8 ), 1024 * 8 );
      }
    }

    if ($file) {
      fclose($file);
    }

    if ($newf) {
      fclose($newf);
    }
?>
</html> 

これは、ユーザーが既存のPHPアプリケーション内で機能するソリューションではなくスタンドアロンスクリプトを必要としていることを前提としています。OPや他のほとんどのユーザーが探しているのは後者です。説明は、アプローチを理解したい人にも役立ちます。
Beanをショーン

1
これを試すたびに、転送されたファイルサイズは常に50816ですが、ファイルサイズはこれよりも大きくなります。120MB..これはなぜですか?
Riffaz Starr

set_time_limit (24 * 60 * 60);ループ内に配置する必要があります。スクリプトの最初には影響しません。
Viktor

16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);

あなたの答えは非常にシンプルでうまく機能しており、cURLがファイルの取得に失敗した場合に役立ちました。これはうまくいきました。ありがとう:)
Tommix

2
これが実際に何をしているのかを説明したいと思うかもしれません。
アレックス

6
これは、PHPのメモリ制限を超えるというOPの問題には対応していません。
user9645

これは非常にシンプルで簡単です。ファイルが小さい、または環境がローカル開発である、より単純な場合に非常に役立ちます。
バレンタイン市

.xlsxファイルのアイデアはありますか?メモリが0バイトの空のファイルを保存しています。
Dhruv Thakkar


8

PHPで単純なメソッドを使用する copy()

copy($source_url, $local_path_with_file_name);

注:宛先ファイルがすでに存在する場合は、上書きされます

PHPのcopy()関数

注:宛先フォルダーに許可777を設定する必要があります。ローカルマシンにダウンロードする場合は、この方法を使用します。

特記事項:777は、所有者、グループ、および全員に対する完全な読み取り/書き込み/実行権限を持つUnixベースのシステムの権限です。通常、この許可は、Webサーバー上で非公開にする必要のないアセットに付与されます。例:画像フォルダ。


1
私は決して777をWebサーバー上でパーマとして設定することは決してありませんし、それを行うのが悪い考えを持っているWeb開発者を開始します。いつでも、どこでも。気をつけて!それをしてはいけない !セキュリティについて考えます。OWASPルールに従うだけでは十分ではありません。単純なことについてよく考えることは重要です。
ThierryB

@ThierryB。注:ローカルパスを指定しました。&これは内部アプリケーションで使用できます。質問と回答の問題をよく読み、理解する。さまざまなシナリオを考えてください。そして、これは受け入れられません/最良の答えです。すべての質問には長所と短所があり、さまざまな答えがあります。理解するための例:フィボナッチでさえ、1つだけが最善である複数のユニークなソリューションがあります。その他は、さまざまなシナリオで使用されます。
Pradeep Kumar Prabaharan

わかりましたが、ベストプラクティスについて考え、セキュリティで保護された場所に実装することで、実装する必要がある概念をよりよく理解できます。多分、侵入者があなたの($)家の中にいる場合、いくつかのトラップを行うか、最善の方法で物事を構築すると、彼にいくつかの頭痛がします;)
ThierryB

5

これを使用してファイルをダウンロードします

function cURLcheckBasicFunctions()
{
  if( !function_exists("curl_init") &&
      !function_exists("curl_setopt") &&
      !function_exists("curl_exec") &&
      !function_exists("curl_close") ) return false;
  else return true;
}

/*
 * Returns string status information.
 * Can be changed to int or bool return types.
 */
function cURLdownload($url, $file)
{
  if( !cURLcheckBasicFunctions() ) return "UNAVAILABLE: cURL Basic Functions";
  $ch = curl_init();
  if($ch)
  {

    $fp = fopen($file, "w");
    if($fp)
    {
      if( !curl_setopt($ch, CURLOPT_URL, $url) )
      {
        fclose($fp); // to match fopen()
        curl_close($ch); // to match curl_init()
        return "FAIL: curl_setopt(CURLOPT_URL)";
      }
      if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_HEADER, $curlopt_header)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects) ) return "FAIL: curl_setopt(CURLOPT_MAXREDIRS)";

        return curl_exec($ch);
    } else {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_HEADER, true)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_RETURNTRANSFER, true)) return "FAIL: curl_setopt(CURLOPT_RETURNTRANSFER)";
        if( !curl_setopt($ch, CURLOPT_FORBID_REUSE, false)) return "FAIL: curl_setopt(CURLOPT_FORBID_REUSE)";
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
    }
      // if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true) ) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
      // if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
      // if( !curl_setopt($ch, CURLOPT_HEADER, 0) ) return "FAIL: curl_setopt(CURLOPT_HEADER)";
      if( !curl_exec($ch) ) return "FAIL: curl_exec()";
      curl_close($ch);
      fclose($fp);
      return "SUCCESS: $file [$url]";
    }
    else return "FAIL: fopen()";
  }
  else return "FAIL: curl_init()";
}

4

PHP 4&5ソリューション:

readfile()は、大きなファイルを送信する場合でも、それ自体ではメモリの問題を引き起こしません。fopenラッパーが有効になっている場合、この関数でファイル名としてURLを使用できます。

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


1
質問は出力バッファではなくディスクに書き込むことに関するものなので、これは質問に答えません。
Lorenz Meyer
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.