2011-06-29 28 views
26

Sử dụng PHP, tôi đang cố gắng phân phối các tệp lớn (tối đa 200MB) không nằm trong thư mục có thể truy cập web do các vấn đề về ủy quyền. Hiện tại, tôi sử dụng cuộc gọi readfile() cùng với một số tiêu đề để phân phối tệp, nhưng có vẻ như PHP đang tải nó vào bộ nhớ trước khi gửi. Tôi dự định triển khai trên một máy chủ lưu trữ được chia sẻ, điều này sẽ không cho phép tôi sử dụng nhiều bộ nhớ hoặc thêm các mô-đun Apache của riêng tôi như X-Sendfile.Cách tải xuống các tệp lớn thông qua tập lệnh PHP

Tôi không thể để các tệp của mình nằm trong thư mục có thể truy cập web vì lý do bảo mật. Có ai biết một phương pháp đó là ít bộ nhớ chuyên sâu mà tôi có thể triển khai trên một máy chủ lưu trữ được chia sẻ?

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

Không đồng ý điều này trùng lặp, anh chàng này đang cố gắng tải xuống qua PHP, khi câu hỏi khác chỉ là cách tải xuống tệp lớn .. các vấn đề khác nhau một cách trung thực. –

Trả lời

10

Nếu bạn sử dụng fopenfread thay vì readfile, mà nên giải quyết vấn đề của bạn.

a solution trong tài liệu readfile của PHP cho biết cách sử dụng fread để thực hiện những gì bạn muốn.

+0

Sử dụng một readfile chunked với 'flush()' và 'ob_flush()' là loại làm việc như đã nêu trong tài liệu. Bây giờ tôi đang gặp rắc rối với việc tải xuống chỉ bị gián đoạn ở gần cuối của tập tin. Có ý tưởng nào không? – brenns10

+0

@ brenns10 - Ví dụ không sử dụng 'flush()' và 'ob_flush()'. Sử dụng chúng sẽ làm cho dữ liệu được chuyển vào bộ nhớ cho đến khi nó được hiển thị, một cái gì đó bạn có thể muốn tránh nếu bạn có tài nguyên giới hạn. Với điều đó đã nói, tôi không chắc chắn lý do tại sao nó bỏ qua cuối mà không nhìn thấy mã của bạn.Có thể là một vài byte cuối cùng của tệp không được đọc hoặc bạn không bị xóa sau lần đọc cuối cùng. –

+0

Tôi đã thử sử dụng tập lệnh cả với lệnh tuôn ra và không có, và khi tôi sử dụng chúng, quá trình tải xuống bắt đầu ngay lập tức và không sử dụng nhiều bộ nhớ (trên WAMP của tôi). Khi tôi không sử dụng lệnh tuôn ra, có một sự chậm trễ khá lâu trước khi quá trình tải xuống bắt đầu (trong đó, tôi giả định, là PHP đọc tệp vào bộ nhớ), và trong thời gian đó có một sự tăng đột biến RAM lớn. Tôi sẽ đăng mã tôi đang sử dụng trong câu hỏi ban đầu. – brenns10

2

Bạn cũng có thể xử lý điều này theo kiểu của nút Gordian - tức là, hãy tránh hoàn toàn vấn đề. Giữ các tập tin trong một thư mục không sử dụng được, và khi một download được bắt đầu, bạn có thể chỉ đơn giản là

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

và thiết lập một cronjob để unlink() bất kỳ liên kết tải lớn hơn 10 phút. Hầu như không xử lý dữ liệu của bạn là bắt buộc, không cần phải xoa bóp tiêu đề HTTP, v.v.

Thậm chí còn có một vài thư viện chỉ có mục đích này.

3

Nếu bạn quan tâm đến hiệu suất, có xsendfile, có sẵn trong apache, nginx và lighttpd làm mô-đun. Kiểm tra các ý kiến ​​của người dùng tài liệu readfile() doc.

Ngoài ra còn có các mô-đun cho các máy chủ web này chấp nhận url có giá trị băm bổ sung cho phép tải xuống tệp trong một khoảng thời gian ngắn. Điều này cũng có thể được sử dụng để giải quyết các vấn đề ủy quyền.

+0

Điều này nghe có vẻ giống như một lựa chọn hữu ích, nhưng đây có phải là thứ mà tôi có thể thực hiện trên một máy chủ lưu trữ dùng chung không? Tôi nghĩ rằng các công ty lưu trữ có một vài mô-đun thiết lập mà họ sử dụng, vì vậy tôi có thể sẽ không thể sử dụng điều này trên một máy chủ chia sẻ. – brenns10

+0

Tốt, bạn đã trả lời câu hỏi của bạn :) Hãy hỏi họ .. –

4

Để download các file lớn từ máy chủ, tôi đã thay đổi các thiết lập dưới đây trong tập tin php.ini:

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

Bây giờ, tôi có thể tải lên và tải video 175MB trên máy chủ. Vì tôi có máy chủ chuyên dụng. Vì vậy, việc thực hiện những thay đổi này thật dễ dàng.

Dưới đây là tập lệnh PHP để tải xuống tệp. Tôi không thực hiện bất kỳ thay đổi nào trong đoạn mã này cho kích thước tệp lớn.

// 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; 
Các vấn đề liên quan