6

私は強制ダウンロードを使用して、私が行ったサイト(http://pr1pad.kissyour.net)で主にzipとmp3をダウンロードしています-グーグルアナリティクス、データベースでダウンロードを追跡し、実際のダウンロードパスを非表示にします:

これです:

extending CI model

... - bunch of code

function _fullread ($sd, $len) {
 $ret = '';
 $read = 0;
 while ($read < $len && ($buf = fread($sd, $len - $read))) {
  $read += strlen($buf);
  $ret .= $buf;
 }
 return $ret;
}

function download(){    
    /* DOWNLOAD ITSELF */

    ini_set('memory_limit', '160M');
    apache_setenv('no-gzip', '1');
    ob_end_flush();

    header("Pragma: public");
    header("Expires: 0");
    header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
    header("Cache-Control: public",FALSE);
    header("Content-Description: File Transfer");
    header("Content-type: application/octet-stream");
     if (isset($_SERVER['HTTP_USER_AGENT']) && 
      (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false))
      header('Content-Type: application/force-download'); //IE HEADER
    header("Accept-Ranges: bytes");
    header("Content-Disposition: attachment; filename=\"" . basename("dir-with-    files/".$filename) . "\";");
    header("Content-Transfer-Encoding: binary");
    header("Content-Length: " . filesize("dir-with-files/".$filename));

    // Send file for download
    if ($stream = fopen("dir-with-files/$filename", 'rb')){
     while(!feof($stream) && connection_status() == 0){
      //reset time limit for big files
      set_time_limit(0);
      print($this->_fullread($stream,1024*16));
      flush();
     }
     fclose($stream);
    }
}

それはCI1.7.2のLAMP上にあります-開発中にこれらの問題が発生したため、これはインターネット全体のさまざまなハウツーからまとめられた私自身の方法です:-サーバー制限。 サイトがCI1.7.2で行われ、バッファをクリーンアップする必要があったため、-ob_end_flush()の代わりに通常のini_setバッファを使用しました。_fullreadfread@readonly

今...それは動作しません。すると、予想されるサイズ/ダウンロード時間が表示されなくなりました-クリーンアップしようとしましたが、コードをクリーンアップしているときに、何かが発生しました以前のバージョンでは、機能しませんでした(設定の変更)-編集:機能しない=すべてをブラウザウィンドウに出力します。

だから私は言った、それをねじ込み、私はここを見ていきます。

したがって、私は基本的にスクリプトまたは関数を探します。これを出力モデルに配置して、次のことを実行します。

  • 強制ダウンロードを呼び出す(Chromeではダウンロードを開始し、IE、FFではSafariでモーダルオープン/保存/キャンセルを開きます)
  • ファイルのサイズと推定dl時間を表示します(これはブラウザ次第ですが、最初に、ブラウザはファイルサイズを知っている必要があります
  • IE6、7、8、FF3、Opera、Chrome、およびPC + Mac(Linux ...私は本当に気にしない)のサファリでの作業(テストおよび確認済み!)-これはヘッダー部分です
  • サーバー上では、56MBのメモリ制限のようなものもありますが、これを追加することはできません。これも重要です。

前もって感謝します。

編集:.htaccessでダウンロードを強制しようとしたので、今まで以上に厄介な気分になりました-それが機能している間、マイナー/メジャー(あなたを選んでください)の問題はほとんどありませんでした

  • それはフルパスを示しました(私にとってはマイナー)
  • ダウンロード全体が完了するまで待機し(「接続中」と表示)、ダウンロード中であることを表示します。1秒でダウンロードします(私にとってはメジャー)

ここで、.htaccessを削除しましたが、ダウンロードが完了するまで待機し(最初にキャッシュにダウンロードした場合と同じように)、取得してconnected[開く/保存]ダイアログを表示します。

4

7 に答える 7

8

だから、私はこのコードを使用しました(インターネットで見つかった再開可能なhttpダウンロードの修正版です)

function _output_file($file, $path)
{
    $size = filesize($path.$file);

    @ob_end_clean(); //turn off output buffering to decrease cpu usage

    // required for IE, otherwise Content-Disposition may be ignored
    if(ini_get('zlib.output_compression'))
    ini_set('zlib.output_compression', 'Off');

    header('Content-Type: application/force-download');
    header('Content-Disposition: attachment; filename="'.basename($file).'"');
    header("Content-Transfer-Encoding: binary");
    header('Accept-Ranges: bytes');

    /* The three lines below basically make the 
    download non-cacheable */
    header("Cache-control: no-cache, pre-check=0, post-check=0");
    header("Cache-control: private");
    header('Pragma: private');
    header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

    // multipart-download and download resuming support
    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);
    }

    /* output the file itself */
    $chunksize = 1*(1024*1024); //you may want to change this
    $bytes_send = 0;
    if ($file = fopen($path.$file, 'rb'))
    {
        if(isset($_SERVER['HTTP_RANGE']))
        fseek($file, $range);

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

die();
}

そしてモデルで:

function download_file($filename){
    /*
        DOWNLOAD
    */
    $path = "datadirwithmyfiles/"; //directory

    //track analytics

    include('includes/Galvanize.php'); //great plugin
    $GA = new Galvanize('UA-XXXXXXX-7');
    $GA->trackPageView();

    $this->_output_file($filename, $path);

}

Win / MAC 上の上記のすべてのブラウザで期待どおりに動作します。これまでのところ、問題はありません。

于 2010-02-10T21:11:57.900 に答える
2

わかりました、これは古い質問であり、Adam はすでに彼自身の回答を受け入れているため、おそらく彼はこれを自分で機能させることができましたが、なぜ機能したのかについては説明しませんでした。私が気づいたことの 1 つは、彼がヘッダーを使用したという質問でした。

header("Pragma: public");
header("Cache-Control: public",FALSE);

一方、彼が使用したソリューションでは:

header("Cache-control: private");
header('Pragma: private');

彼はこれらを変更した理由を説明しませんでしたが、SSL の使用に関連していると思われます。私は最近、HTTP と HTTPS の両方でダウンロードを有効にする必要があるソフトウェアで同様の問題を解決し、以下を使用して正しいヘッダーを追加しました。

if(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) {
    header("Cache-control: private");
    header('Pragma: private');
} else {
    header('Pragma: public');
}

うまくいけば、誰かがこの回答の情報を上記に追加するのに役立つと思うでしょう。

于 2011-06-23T10:22:20.157 に答える
1

私が奇妙に思うことが 1 つありob_end_flush()ます。関数の開始時に呼び出しています。これは実際には出力バッファを消去しますが、最初にすべてをクライアントに出力します (CodeIgniter によって設定された Content-Headers を含むと仮定します)。呼び出しをob_end_clean()に変更すると、バッファがクリアされ、破棄されます。これにより、独自のヘッダーを生成するためのクリーンなスタートが得られます。

別のヒント:

ファイルをストリームとして読み取ってブロックごとに渡す代わりに、この関数を試してみてください。

// ...
if (file_exists("dir-with-files/$filename")) {
   readfile($file);
}

これでほぼすべてが処理されます。

于 2010-02-08T19:13:38.830 に答える
0

print($this->_fullread($stream,1024*16));

_fullreadはクラス内にあると思いますか?コードが上記のように見える場合は、$this->機能しません。

すべてのヘッダーをコメントアウトした場合、ファイルの内容が画面に出力されますか?

于 2010-02-08T17:24:53.633 に答える
0

暗闇の中でのショット...「強制ダウンロード」コードで送信するすべてのヘッダー(これはあなたのものほど十分にテストされていません)は、私が呼び出すことを除いて、あなたのものと同じです: header("Cache-Control: private" 、間違い);

代わりに: header("Cache-Control: public",FALSE);

それが役立つかどうかはわかりません。

于 2010-02-08T17:30:36.663 に答える
0

この種の "php で出力する" メソッドを実行しようとすると、ユーザーに残り時間や予想されるサイズを表示できなくなります。なんで?ブラウザーが途中でダウンロードを再開しようとすると、PHP でそのケースを処理する方法がないためです。

通常のファイル ダウンロードの場合、Apache は HTTP 経由でのダウンロードの再開をサポートできますが、ダウンロードが一時停止された場合、Apache は、クライアントが次のチャンクを要求したときに、スクリプトのどこで実行されていたかを把握する方法がありません。

基本的に、ブラウザーがダウンロードを一時停止すると、Web サーバーへの接続が完全に終了します。ダウンロードを再開すると、接続が再開され、リクエストには「バイト番号 X から開始」というフラグが含まれます。しかし、上記の PHP を見ている Web サーバーにとって、バイト X はどこから来たのでしょうか?

理論的には、ダウンロードが中断された場合にサーバーがスクリプトを再開する場所を特定することは可能かもしれませんが、Apache は再開する場所を特定しようとしません。その結果、ブラウザーに送信されるヘッダーは、サーバーがレジュームをサポートしていないことを示しており、これにより、ほとんどの主要なブラウザーで予想されるファイルサイズと制限時間の項目が無効になります。

編集: このケースを処理できるようですが、多くのコードが必要になります。http://www.php.net/manual/en/function.fread.php#84115を参照してください。

于 2010-02-08T18:50:47.313 に答える