PHP - indirmek çok büyük fsockopen ile dosyaları (), fgets () ve feof ()

3 Cevap

Ben bir Amazon Web Services kovadan bir anda megabayt yüzlerce dosyaları ile ilgili olabilecek sınıfta basit bir indirme işlevi var. Tüm dosya anda belleğe yüklenen olamaz, bu yüzden bir dosya işaretçisi doğrudan akışa gerekir. Bu ben bu konu ile ele ettik ve ben birlikte gitmek gibi şeyleri topluyorum ilk kez olarak bu benim anlayış olduğunu.

Ben basit bir test iyi bir boyutu olduğunu gösterdi 4 KB dosya tampon dayanarak, bu ile sona erdi ettik:

        $fs = fsockopen($host, 80, $errno, $errstr, 30);

        if (!$fs) {
          $this->writeDebugInfo("FAILED ", $errstr . '(' . $errno . ')');
        } else {
          $out = "GET $file HTTP/1.1\r\n";
          $out .= "Host: $host\r\n";
          $out .= "Connection: Close\r\n\r\n";
          fwrite($fs, $out);

          $fm = fopen ($temp_file_name, "w");
          stream_set_timeout($fs, 30);

          while(!feof($fs) && ($debug = fgets($fs)) != "\r\n" ); // ignore headers

          while(!feof($fs)) {
            $contents = fgets($fs, 4096); 
            fwrite($fm, $contents);
            $info = stream_get_meta_data($fs);
            if ($info['timed_out']) {
              break;
            }
          }
          fclose($fm);
          fclose($fs);

          if ($info['timed_out']) {
            // Delete temp file if fails
            unlink($temp_file_name);
            $this->writeDebugInfo("FAILED - Connection timed out: ", $temp_file_name);
          } else {
            // Move temp file if succeeds
            $media_file_name = str_replace('temp/', 'media/', $temp_file_name);
            rename($temp_file_name, $media_file_name);
            $this->writeDebugInfo("SUCCESS: ", $media_file_name);
          }
        }

Test iyi. Ancak ben fgets() ve feof() birlikte nasıl çalıştığını anlayarak değilim, ve o daha verimli bir yöntem olarak yığın halinde kodlama söz ediyor olduğunu söyleyerek biriyle içine bir konuşma var.

Kod genellikle Tamam mı, yoksa ben burada çok önemli bir şey eksik? Kodlama bana verecek chunked yararı nedir?

3 Cevap

Sizin çözüm bana güzel görünüyor, ancak ben bir kaç yorum var.

1) HTTP pakete kendiniz oluşturabilirsiniz etmeyin, yani HTTP isteği göndermek yok. Bunun yerine CURL gibi bir şey kullanabilirsiniz. Bu daha aptal kanıt ve sunucu ile cevap olabilecek tepkilerin daha geniş bir destek olacaktır. Ayrıca CURL kendiniz yaparak sizi kurtaran, doğrudan bir dosyaya yazmak için kurulum olabilir.

Ikili verileri okurken eğer 2) fgets kullanarak bir sorun olabilir. Fgets bir satır sonuna kadar okur ve ikili veri ile bu indirme bozabilir. Bunun yerine fread ($ fs, 4096) önermek; metin ve ikili veri hem de idare edecek.

2) Yığın halinde kodlama size birçok parçalar yanıt göndermek için bir web sunucusu için bir yoldur. Ancak, web sunucusu destek olabilecek daha iyi bir kodlama gzip kodlama, bu sizin için çok yararlı olduğunu düşünmüyorum. Bu webserver anında yanıt sıkıştırmak için izin verecek. Eğer CURL gibi bir kitaplık kullanırsanız, o gzip desteklediği sunucusu anlatmak, ve sonra sizin için otomatik olarak sıkıştırması.

Bu yardımcı olur umarım

, Yuva ile anlaşma kodunuzu optimize etmek ve cURL kütüphane, PHP cURL kullanmayın. Bu gibi:

$url = 'http://'.$host.'/'.$file;
// create a new cURL resource
$fh = fopen ($temp_file_name, "w");
$ch = curl_init();
// set URL and other appropriate options
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FILE, $fh); 
//curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
// grab URL and pass it to the browser
curl_exec($ch);
// close cURL resource, and free up system resources
curl_close($ch);
fclose($fh);

Ve sonuç durumda o başka herkes yardımcı olur. Ben de tamamen başarısız indirme riskini azaltmak için bir deneme döngü içinde herşeyi sarılmış, ancak bu kaynakların kullanımını artırmak yapar:

      do {
        $fs = fopen('http://' . $host . $file, "rb");

        if (!$fs) {
          $this->writeDebugInfo("FAILED ", $errstr . '(' . $errno . ')');
        } else {
          $fm = fopen ($temp_file_name, "w");
          stream_set_timeout($fs, 30);

          while(!feof($fs)) {
            $contents = fread($fs, 4096); // Buffered download
            fwrite($fm, $contents);
            $info = stream_get_meta_data($fs);
            if ($info['timed_out']) {
              break;
            }
          }
          fclose($fm);
          fclose($fs);

          if ($info['timed_out']) {
            // Delete temp file if fails
            unlink($temp_file_name);
            $this->writeDebugInfo("FAILED on attempt " . $download_attempt . " - Connection timed out: ", $temp_file_name);
            $download_attempt++;
            if ($download_attempt < 5) {
              $this->writeDebugInfo("RETRYING: ", $temp_file_name);
            }
          } else {
            // Move temp file if succeeds
            $media_file_name = str_replace('temp/', 'media/', $temp_file_name);
            rename($temp_file_name, $media_file_name);
            $this->newDownload = true;
            $this->writeDebugInfo("SUCCESS: ", $media_file_name);
          }
        }
      } while ($download_attempt < 5 && $info['timed_out']);