PHP güvenilir büyük dosyaları indirirken

10 Cevap php

Ben recipents dosya göndermek için bir sunucu üzerinde bir php komut dosyası var: Onlar benzersiz bir bağlantı almak ve sonra da büyük dosyaları indirebilirsiniz. Bazen transferi ile ilgili bir sorun olduğunu ve dosya bozuk ya da bitmez. Büyük dosyaları göndermek için daha iyi bir yolu olup olmadığını merak ediyorum

Kod:

$f = fopen(DOWNLOAD_DIR.$database[$_REQUEST['fid']]['filePath'], 'r');
while(!feof($f)){
    print fgets($f, 1024);
}
fclose($f);

Ben gibi fonksiyonları gördük

http_send_file
http_send_data

Ama onlar çalışacaktır eğer emin değilim.

Bu sorunu çözmek için en iyi yolu nedir?

Regards
erwing

10 Cevap

Eğer bu olacak gerçekten büyük dosyaları ve etkisi hakkında endişe gönderiyor iseniz, x-dosya gönderme başlığını kullanabilirsiniz.

SOq kaynaktan using-xsendfile-with-apache-php, bir belgede; blog.adaniels.nl : how-i-php-x-sendfile/

En iyi çözüm lighty veya apache güvenmek olacaktır, ancak PHP, ben (vb tekerleği yeniden icat etmeye gerek yok) PEAR's HTTP_Download kullanmak istiyorsunuz, gibi, bazı güzel özelliklere sahiptir:

  • Temel azaltma mekanizması
  • Aralıkları (kısmi indirme ve sürdürme)

Bkz intro/usage docs.

Ben geçmişte bu yapmış ben bu kullandım:

set_time_limit(0); //Set the execution time to infinite.
header('Content-Type: application/exe'); //This was for a LARGE exe (680MB) so the content type was application/exe
readfile($fileName); //readfile will stream the file.

Bu kod 3 satır readfile() müşteriye belirtilen tüm dosya akışı olacak download tüm çalışmaları yapmak ve dosya önce zaman azalıyor olabilir başka sonsuz bir zaman sınırı ayarlamak için emin olacaktır bitmiş akışı olduğunu.

Gerçek dosya sembolik bir bağlantı oluşturun ve sembolik linkten download linki noktaya olun. Ardından, kullanıcı DL linke tıkladığında, onlar sembolik bağlantısından adlı gerçek dosyasından ancak bir dosya yüklemeyi alırsınız. Bu sembolik bir bağlantı oluşturmak için milisaniye sürer ve yeni bir isimle dosya kopyalama ve oradan indirmek için çalışırken daha iyidir.

Örneğin:

<?php

// validation code here

$realFile = "Hidden_Zip_File.zip";
$id = "UserID1234";

if ($_COOKIE['authvalid'] == "true") {
    $newFile = sprintf("myzipfile_%s.zip", $id); //creates: myzipfile_UserID1234.zip

    system(sprintf('ln -s %s %s', $realFile, $newFile), $retval);

    if ($retval != 0) {
        die("Error getting download file.");
    }

    $dlLink = "/downloads/hiddenfiles/".$newFile;
}

// rest of code

?>

<a href="<?php echo $dlLink; ?>Download File</a>

Bu Go Daddy 2 dakika 30 saniye kadar sonra .... bu bu sorunu önler ve gerçek dosyayı gizler çalışan komut dosyası öldürür çünkü ben yaptım.

Daha sonra kurulum düzenli aralıklarla sembolik bağlantıları silmek için bir CRON işi olabilir ....

Bütün bu süreç, daha sonra dosyayı tarayıcıya gönderir ve bunun bir senaryo değil, çünkü ishal ne kadar önemli değildir.

Biz projeleri bir çift bu kullanarak oldum ve şimdiye kadar oldukça iyi çalışıyor:

/**
 * Copy a file's content to php://output.
 *
 * @param string $filename
 * @return void
 */
protected function _output($filename)
{
    $filesize = filesize($filename);

    $chunksize = 4096;
    if($filesize > $chunksize)
    {
        $srcStream = fopen($filename, 'rb');
        $dstStream = fopen('php://output', 'wb');

        $offset = 0;
        while(!feof($srcStream)) {
            $offset += stream_copy_to_stream($srcStream, $dstStream, $chunksize, $offset);
        }

        fclose($dstStream);
        fclose($srcStream);   
    }
    else 
    {
        // stream_copy_to_stream behaves() strange when filesize > chunksize.
        // Seems to never hit the EOF.
        // On the other handside file_get_contents() is not scalable. 
        // Therefore we only use file_get_contents() on small files.
        echo file_get_contents($filename);
    }
}

Dosyaları Aklıma en kolay yolu indirmek için geçici bir konuma dosya koymak ve onlara düzenli HTTP üzerinden indirebilirsiniz benzersiz bir URL vermek olacaktır.

Bu bağlantıları üreten bir parçası olarak da X saat eski daha vardı dosyaları kaldırmak olabilir.

Eğer bir web sunucusu olarak lighttpd kullanıyorsanız, güvenli yüklemeler için bir alternatif kullanmak olacaktır ModSecDownload. Bu sunucu yapılandırma ihtiyacı ancak webserver yerine PHP script indir kendisi idare izin vereceğim.

Indirme URL oluşturuluyor böyle olmazdı (belgelerine alınan) ve tabii ki sadece yetkili kullanıcılar için oluşturulmuş olabilir:

<?php

  $secret = "verysecret";
  $uri_prefix = "/dl/";

  # filename
  # please note file name starts with "/" 
  $f = "/secret-file.txt";

  # current timestamp
  $t = time();

  $t_hex = sprintf("%08x", $t);
  $m = md5($secret.$f.$t_hex);

  # generate link
  printf('<a href="%s%s/%s%s">%s</a>',
         $uri_prefix, $m, $t_hex, $f, $f);
?>

Tabii ki, Unkwntech mükemmel önerdiği readfile() gibi kullanarak, dosyaların boyutuna bağlı olarak değişebilir. Ve tarafından önerildiği gibi xsendfile kullanarak garrow zamanda Apache tarafından desteklenen başka bir iyi bir fikirdir.

Ben bu büyük dosyalar için iyi bir fikir olduğundan emin değilim. Kullanıcı yüklemeyi tamamladı ve Apache gibi bir şey koşuyoruz kadar indirme komut için iplik biterse Apache uzun soluklu çok sayıda çalıştırmak için tasarlanmış nedeniyle, sadece 50 veya daha fazla eşzamanlı indirme, sunucu çökmesine olabilir Aynı zamanda lifler. Apache iplik bir şekilde sona erer ve yükleme ilerledikçe iken indirme yerde bir tampon oturur tabii eğer, yanlış olabilir.

Ben readfile için php manuel giriş açıklamalarda bulunan aşağıdaki kod parçasını kullanmış:

function _readfileChunked($filename, $retbytes=true) {
    $chunksize = 1*(1024*1024); // how many bytes per chunk
    $buffer = '';
    $cnt =0;
    // $handle = fopen($filename, 'rb');
    $handle = fopen($filename, 'rb');
    if ($handle === false) {
        return false;
    }
    while (!feof($handle)) {
        $buffer = fread($handle, $chunksize);
        echo $buffer;
        ob_flush();
        flush();
        if ($retbytes) {
            $cnt += strlen($buffer);
        }
    }
    $status = fclose($handle);
    if ($retbytes && $status) {
        return $cnt; // return num. bytes delivered like readfile() does.
    }
    return $status;
}

Dosyaları Parçalama PHP hızlı / basit yöntem, size olamaz ya, mod-xsendfile cURL gibi biraz daha profesyonel bir şey yararlanmak istemiyorsanız {[(2 )]} ya da bazı dedicated script.

$filename = $filePath.$filename;

$chunksize = 5 * (1024 * 1024); //5 MB (= 5 242 880 bytes) per one chunk of file.

if(file_exists($filename))
{
    set_time_limit(300);

    $size = intval(sprintf("%u", filesize($filename)));

    header('Content-Type: application/octet-stream');
    header('Content-Transfer-Encoding: binary');
    header('Content-Length: '.$size);
    header('Content-Disposition: attachment;filename="'.basename($filename).'"');

    if($size > $chunksize)
    { 
        $handle = fopen($filename, 'rb'); 

        while (!feof($handle))
        { 
          print(@fread($handle, $chunksize));

          ob_flush();
          flush();
        } 

        fclose($handle); 
    }
    else readfile($path);

    exit;
}
else echo 'File "'.$filename.'" does not exist!';

Taşıdık richnetapps.com / NeedBee. readfile() bile 1G, bu indirilen dosya boyutu beş kat daha fazla olduğu için ayarlanmış izin verilen maksimum bellek sınırı ile, öldüğü 200 MB dosyaları üzerinde test edilmiştir.

BTW: Ben dosyaları >2GB Ayrıca bu test, ama PHP sadece 2GB dosyanın ilk yazmak başardı ve ardından bağlantıyı kırdı. Dosya ile ilgili fonksiyonlar (fopen, fread, fseek) sonuçta bir sınıra yani, INT kullanır 2GB. Yukarıda sözü edilen çözümler (örneğin, mod-xsendfile), bu durumda, tek seçenek olarak görünmektedir.

EDIT: Make yourself 100% dosya kaydedilir ki utf-8. Bunu atlarsanız, indirilen dosyalar bozuk olacaktır. Bu çözümler kullandığı print, tarayıcıya bir dosya yığın itmek, çünkü bu, bir.