PHP idiot-proof, çapraz tarayıcı force indir

7 Cevap php

Ben (http://pr1pad.kissyour.net) yoktu sitede çoğunlukla fermuarlar ve mp3 indirmek zorunda indir istimal - veritabanı, google analytics indirmeleri izlemek ve gerçek indirme yolunu gizlemek için:

Bu var:

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);
    }
}

It's on LAMP with CI 1.7.2 - It's my own method put together from various how-tos all over the internet, because during developement, these problems came up: - server limit. ini_set haven't helped, so I used buffered _fullread instead normal fread, which was used insted of @readonly - ob_end_flush(), because site is did in CI1.7.2 and i needed to clean buffer

Şimdi ... Bu işe yaramazsa. Ben kodu temizlik yaparken ben onu temizlemek için çalıştı ve bir şey oldu, ben ne ve any önceki sürümü bilmiyorum - - Bu o beklenen boyutu / indirme süresini gösteren durdu yaptım, o (herhangi ayarlarında değişiklik yok) çalıştı değil - edit: işe yaramayan = tarayıcı penceresinin içine her şeyi verir.

Yani ben burada bakacağım, o vida, dedi.

Yani, temelde benim çıkış modeli koyabilirsiniz ve yapacak olan komut veya fonksiyon arayın:

  • Call force-download (in Chrome start download, in IE,FF,Safari open the modal open/save/cancel)
  • Dosyanın Göster boyutu ve tahmini dl zaman (bu tarayıcıda kalmış, biliyorum, ama önce, tarayıcı dosya boyutu bilmeniz gerekir
  • İŞ (test ve teyit!) IE6, 7,8, FF3, Opera, Chrome ve içinde ve PC + Mac safari (Linux. .. ben gerçekten umurumda değil) - bu başlık kısmı için var
  • olması da önemlidir böylece sunucu üzerinde, ben de ben eklemek olamaz 56MB bellek sınırı, gibi bir şey var

Şimdiden teşekkür ederim.

. Edit: Şimdi daha Htaccess ile indir zorlamaya çalıştı beri, her zamankinden daha önce / vidalı hissediyorum - çalışırken, bir kaç büyük / küçük vardı (seninkini almak) sorunları

  • tam yolunu (küçük benim için) gösterdi
  • ve bir saniye içinde İndir (büyük benim için) - bu tam indir ("bağlantı" olarak gösteren) tamamladı ve ardından sadece indiriyor gösterecek kadar bekler

Ben. Htaccess silindi rağmen indir (ilk önbelleğe indirirken sadece sanki) tamamlandı ve sadece connected bulunuyor almak ve açık / iletişim kaydetmek göstermek kadar, şimdi, hala bekler.

7 Cevap

Yani, (Bu internet üzerinde bulunan resumable http indir değiştirilmiş bir sürümü var) bu kod kullanılır

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();
}

ve model:

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 tüm mentiond tarayıcısında beklendiği gibi çalıştığını - Şimdiye kadar, onunla hiçbir sorun.

Tamam, bu eski bir sorudur ve Adam zaten kendi cevabını kabul, bu yüzden muhtemelen kendisi için bu çalışma var, ama çalıştı neden açıklama yapmadı. Ben fark bir şey o başlıklarını kullanılan söz vardı:

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

O kullanılan çözeltide ise:

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

O bu neden değişti o açıklama yapmadı ama SSL kullanımı ile ilgilidir sanıyorum. Geçenlerde doğru başlık eklemek için aşağıdaki kullanılarak, HTTP ve HTTPS hem aşırı yüklemeyi etkinleştirmek için gereken yazılım benzer bir sorunu çözmüş:

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

Umarım birileri bu cevap üzerinde yararlı bir ek bilgileri bulabilirsiniz.

Ben garip buluyorum tek bir şey var: Bu fonksiyon başında ob_end_flush() çağırıyor. Bu aslında Çıktı tamponunu temizler, aynı zamanda (Ben CodeIgniter tarafından belirlenen İçerik Başlıkları dahil varsayıyorum) ilk istemci için her şeyi verir. Çağrısı ob_end_clean(), bu tampon temizler ve onu atar değiştirin. Bu size kendi başlıklarını oluşturmak için temiz bir başlangıç ​​verecektir.

Başka bir ipucu:

Bunun yerine bir akım olarak dosya okuma ve blok-bilge geçirmeden, bu fonksiyonunu bir deneyin verebilir:

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

Bu hemen hemen her şeyi halleder.

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

Ben _fullread bir sınıf içinde olduğunu varsayalım? Kod yukarıdaki gibi görünüyorsa o zaman $this-> işe yaramaz.

Eğer başlık şeyler tüm dışarı yorumladı ekrana o çıktı dosya içeriği mu?

Just a shot in the dark... every header that I send in my 'force download' code (which is not as well tested as yours) is the same as yours, except I call: header("Cache-Control: private",false);

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

O ya da yardımcı olmaz ise ben bilmiyorum.

Eğer "php ile dışarı Echo" yöntemi bu tür yapmak için gidiyoruz, o zaman kullanıcılar için bir kalan süre, ya da beklenen bir boyutunu göstermek mümkün olmayacaktır. Neden? Tarayıcı ortasına indirmek devam çalışırsa, PHP bu davayı taşıma yolu yoktur çünkü.

Eğer normal bir dosya indirme varsa, Apache HTTP üzerinden indirme devam destekleme yeteneğine sahip, ancak bir yükleme duraklatıldı durumunda, Apache bir istemci sonraki öbek sorduğunda komut işler yürütme nerede sergiyi bir yolu vardır.

Bir tarayıcı bir indirme duraklar Esasen, tamamen webserver bağlantısı sonlandırılacaktır. Eğer indirme devam ettirdiğinizde, bağlantı yeniden ve istek "bayt sayısı X başlayın" diyerek bir bayrak içerir. Ama yukarıdaki PHP bakarak webserver, nerede bayt X nereden geliyor?

Teoride bir kesintiye indirme durumunda sizin komut devam nerede belirlemek için sunucu için mümkün olabilecek iken, Apache yerden devam anlamaya çalışmaz. Sonuç olarak, tarayıcıya gönderilen başlık sunucu çoğu büyük tarayıcılarda beklenen dosya boyutu ve zaman sınırı öğeler kapatılır özgeçmiş, desteklemiyor belirtiyor.

DÜZENLEME: Bu davayı ele almak mümkün olabilir gibi görünüyor, ama bu sizin açınızdan bir sürü kod almaya gidiyor. Bkz http://www.php.net/manual/en/function.fread.php#84115.

Instead of trying to hide Your downloadpath from the world make it inaccessible from outside and only access the files with the above script. to do so you put a htaccess file ( a textfile named '.htaccess' don't forget leading dot) in the directory. Contents of the htaccess would be this:

order deny,allow
deny from all
allow from localhost

Şimdi * dünyadan yolunu erişmeye çalıştığınız web sunucusu bir 401 yasak oluşturmak yapacaktır.

Bilinmezlik aracılığıyla güvenlik ne istediğiniz değil.