2011-06-29 12 views
26

Używając PHP, próbuję wyświetlać duże pliki (maksymalnie 200 MB), które nie znajdują się w katalogu dostępnym w sieci z powodu problemów z autoryzacją. Obecnie używam wywołania readfile() wraz z niektórymi nagłówkami do obsługi pliku, ale wydaje się, że PHP ładuje je do pamięci przed wysłaniem. Zamierzam wdrożyć na współdzielonym serwerze hostingowym, który nie pozwoli mi korzystać z dużej ilości pamięci ani dodawać własnych modułów Apache, takich jak X-Sendfile.Jak pobierać duże pliki za pomocą skryptu PHP

Nie mogę pozwolić, aby moje pliki znajdowały się w katalogu dostępnym w sieci ze względów bezpieczeństwa. Czy ktokolwiek zna metodę, która wymaga mniejszej ilości pamięci, którą mógłbym wdrożyć na współdzielonym serwerze hostingowym?

EDIT:

if(/* My authorization here */) { 
     $path = "/uploads/"; 
     $name = $row[0];   //This is a MySQL reference with the filename 
     $fullname = $path . $name; //Create filename 
     $fd = fopen($fullname, "rb"); 
     if ($fd) { 
      $fsize = filesize($fullname); 
      $path_parts = pathinfo($fullname); 
      $ext = strtolower($path_parts["extension"]); 
      switch ($ext) { 
       case "pdf": 
       header("Content-type: application/pdf"); 
       break; 
       case "zip": 
       header("Content-type: application/zip"); 
       break; 
       default: 
       header("Content-type: application/octet-stream"); 
       break; 
      } 
      header("Content-Disposition: attachment; filename=\"".$path_parts["basename"]."\""); 
      header("Content-length: $fsize"); 
      header("Cache-control: private"); //use this to open files directly 
      while(!feof($fd)) { 
       $buffer = fread($fd, 1*(1024*1024)); 
       echo $buffer; 
       ob_flush(); 
       flush(); //These two flush commands seem to have helped with performance 
      } 
     } 
     else { 
      echo "Error opening file"; 
     } 
     fclose($fd); 
+4

zgadzam jest to duplikat, ten facet próbuje pobrać za pośrednictwem PHP, gdzie, jak inne pytanie brzmi tylko jak pobrać dużego pliku .. różne kwestie uczciwie. –

Odpowiedz

10

Jeśli używasz fopen i fread zamiast readfile, który powinien rozwiązać problem.

Istnieje a solution w dokumentacji PHP readfile pokazującej, jak używać fread do robienia tego, co chcesz.

+0

Używanie fragmentów pliku readfile z 'flush()' i 'ob_flush()' działa w sposób opisany w dokumentacji. Teraz mam problem z pobieraniem, które zostało przerwane pod koniec pliku. Masz jakiś pomysł? – brenns10

+0

@ brenns10 - W przykładzie nie używa się "flush()" i "ob_flush()'. Korzystanie z nich spowoduje, że dane będą przesyłane do pamięci, dopóki nie zostaną wyświetlone, czego możesz chcieć uniknąć, jeśli masz ograniczone zasoby. Powiedziawszy to, nie jestem pewien, dlaczego pomijając koniec, nie widząc swojego kodu.Może to oznaczać, że ostatnie kilka bajtów pliku nie jest odczytywanych lub że po ostatniej lekturze nie spłukujesz się. –

+0

Próbowałem używać skryptu zarówno przy użyciu komend do spłukiwania, jak i bez, a kiedy ich używam, pobieranie rozpoczyna się natychmiast i nie zużywa dużo pamięci (na moim WAMP). Kiedy nie używam komend do spłukiwania, jest duże opóźnienie przed rozpoczęciem pobierania (co, jak przypuszczam, jest odczytywaniem pliku w pamięci), i w tym czasie występuje ogromny skok pamięci RAM. Opublikuję kod, którego używam w pierwotnym pytaniu. – brenns10

2

Można sobie z tym poradzić w stylu węzła gordyjskiego, to znaczy całkowicie obejść problem. Trzymać pliki w niedostępnej katalogu, a podczas pobierania jest inicjowana może po prostu

tempstring = rand(); 
symlink('/filestore/filename.extension', '/www/downloads'.tempstring.'-filename.extension'); 
echo("Your download is available here: <a href='/downloads/'.tempstring.'-filename.extension'); 

i Skonfiguruj zadanie crona do unlink() jakieś linki do pobrania starsze niż 10 minut ty. Praktycznie żadne przetwarzanie danych nie jest wymagane, nie ma masowania nagłówków HTTP itp.

Istnieje nawet kilka bibliotek, które służą właśnie temu celowi.

3

Jeśli zależy Ci na wydajności, dostępny jest plik xsend, dostępny w apache, nginx i lighttpd jako moduł. Sprawdź komentarze użytkowników do dokumentu readfile().

Istnieją również moduły dla tych serwerów internetowych, które akceptują adres URL z dodatkową wartością skrótu, która pozwala pobrać plik przez krótki czas. Może to również służyć do rozwiązywania problemów z autoryzacją.

+0

To brzmi jak przydatna alternatywa, ale czy jest to coś, co mogę zrobić na wspólnym serwerze hostingowym? Myślę, że firmy hostingowe mają kilka zestawów modułów, których używają, więc prawdopodobnie nie będę mógł ich używać na współdzielonym serwerze. – brenns10

+0

Dobrze, już odpowiedziałeś na swoje pytanie :) Zapytaj ich .. –

4

Aby pobrać duże pliki z serwera, zmieniłem poniższych ustawień w pliku php.ini:

Upload_max_filesize - 1500 M 
Max_input_time - 1000 
Memory_limit - 640M 
Max_execution_time - 1800 
Post_max_size - 2000 M 

Teraz jestem w stanie wysyłać i pobierać 175 MB wideo na serwerze. Ponieważ mam serwer dedykowany. Tak więc, wprowadzanie tych zmian było łatwe.

Poniżej znajduje się skrypt PHP do pobrania pliku. Nie wprowadziłem żadnych zmian w tym fragmencie kodu w przypadku dużego rozmiaru pliku.

// Begin writing headers 

      ob_clean(); // Clear any previously written headers in the output buffer 

      if($filetype=='application/zip') 
      { 
       if(ini_get('zlib.output_compression')) 
        ini_set('zlib.output_compression', 'Off'); 
       $fp = @fopen($filepath, 'rb'); 
       if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) 
       { 
        header('Content-Type: "$content_type"'); 
        header('Content-Disposition: attachment; filename="'.$filename.'"'); 
        header('Expires: 0'); 
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 
        header("Content-Transfer-Encoding: binary"); 
        header('Pragma: public'); 
        header("Content-Length: ".filesize(trim($filepath))); 
       } 
       else 
       { 
        header('Content-Type: "$content_type"'); 
        header('Content-Disposition: attachment; filename="'.$filename.'"'); 
        header("Content-Transfer-Encoding: binary"); 
        header('Expires: 0'); 
        header('Pragma: no-cache'); 
        header("Content-Length: ".filesize(trim($filepath))); 
       } 

       fpassthru($fp); 
       fclose($fp); 
      } 
      elseif($filetype=='audio'|| $filetype=='video') 
      { 
       global $mosConfig_absolute_path,$my; 
       ob_clean(); 
       header("Pragma: public"); 
       header('Expires: 0'); 
       header('Cache-Control: no-store, no-cache, must-revalidate'); 
       header('Cache-Control: pre-check=0, post-check=0, max-age=0'); 
       header("Cache-Control: public");  
       header("Content-Description: File Transfer"); 
       header("Content-Type: application/force-download"); 
       header("Content-Type: $content_type"); 
       header("Content-Length: ".filesize(trim($filepath))); 
       header("Content-Disposition: attachment; filename=\"$filename\""); 
       // Force the download   
       header("Content-Transfer-Encoding: binary");    
       @readfile($filepath); 
      } 
       else{ // for all other types of files except zip,audio/video 
       ob_clean(); 
       header("Pragma: public"); 
       header('Expires: 0'); 
       header('Cache-Control: no-store, no-cache, must-revalidate'); 
       header('Cache-Control: pre-check=0, post-check=0, max-age=0'); 
       header("Cache-Control: public");  
       header("Content-Description: File Transfer"); 
       header("Content-Type: $content_type"); 
       header("Content-Length: ".filesize(trim($filepath))); 
       header("Content-Disposition: attachment; filename=\"$filename\""); 
       // Force the download   
       header("Content-Transfer-Encoding: binary");    
       @readfile($filepath);  
      } 
      exit; 
Powiązane problemy