Tải tập tin về máy chủ từ URL


341

Vâng, điều này có vẻ khá đơn giản, và nó là. Tất cả những gì bạn phải làm để tải tệp xuống máy chủ của mình là:

file_put_contents("Tmpfile.zip", file_get_contents("http://someurl/file.zip"));

Chỉ có một vấn đề. Điều gì nếu bạn có một tệp lớn, như 100mb. Sau đó, bạn sẽ hết bộ nhớ và không thể tải xuống tệp.

Những gì tôi muốn là một cách để ghi tệp vào đĩa khi tôi đang tải nó. Bằng cách đó, tôi có thể tải xuống các tệp lớn hơn mà không gặp vấn đề về bộ nhớ.


4
Điều đó được thiết lập trong cấu hình máy chủ của bạn, PHP thực sự không thể khắc phục được như tôi biết (ngoại trừ bản chỉnh sửa .ini trực tiếp)
Ben

Câu trả lời:


494

Kể từ PHP 5.1.0, file_put_contents()hỗ trợ viết từng mảnh bằng cách chuyển một xử lý luồng làm $datatham số:

file_put_contents("Tmpfile.zip", fopen("http://someurl/file.zip", 'r'));

Từ hướng dẫn:

Nếu dữ liệu [đó là đối số thứ hai] là tài nguyên luồng, bộ đệm còn lại của luồng đó sẽ được sao chép vào tệp đã chỉ định. Điều này tương tự với việc sử dụng stream_copy_to_stream().

(Cảm ơn Hakre .)


4
Đó không phải là lựa chọn đầu tiên của tôi. Nếu allow_fopen_url Offđược đặt trong php.ini (ý tưởng tốt cho bảo mật), tập lệnh của bạn sẽ bị hỏng.
Vui lòng khởi động vào

4
@idealmachine Tôi nghĩ rằng file_get_contents()nó cũng không hoạt động nếu đó là trường hợp (xem OP).
alex

10
@geoff Tôi đã nói cụ thể, tôi đã đề cập đến chức năng bạn muốn. Những gì bạn có thể muốn là ai đó viết mã cho bạn - nhưng tôi chắc chắn bạn đã học được điều gì đó tự làm. Ngoài ra, nếu chúng tôi sẽ bình luận về các tương tác SO của nhau - vui lòng chấp nhận thêm một số câu trả lời :)
alex

@alex: Vui lòng xem chỉnh sửa, vui lòng kết hợp. cho tôi biết khi nào tôi có thể xóa bình luận này ở đây
hakre

4
Cờ 'b' cũng nên được sử dụng trong hầu hết các trường hợp với fopen; ngăn chặn các tác động bất lợi cho hình ảnh và các tệp văn bản không đơn giản khác.
Wayne Weibel

132
private function downloadFile($url, $path)
{
    $newfname = $path;
    $file = fopen ($url, 'rb');
    if ($file) {
        $newf = fopen ($newfname, 'wb');
        if ($newf) {
            while(!feof($file)) {
                fwrite($newf, fread($file, 1024 * 8), 1024 * 8);
            }
        }
    }
    if ($file) {
        fclose($file);
    }
    if ($newf) {
        fclose($newf);
    }
}

1
cảm ơn vì đoạn trích của bạn, nhưng bạn có thể giải thích mã của bạn @xaav không? Tôi không thực sự xuất sắc ở php. 1024 * 8 để làm gì? Cảm ơn một lần nữa.
vvMINOvv

@wMINOw Chiều dài của dòng.
David Bélanger

2
Cụ thể, nó có nghĩa là đọc tối đa 8KB tại một thời điểm (1024 byte mỗi KB * 8) vì tham số được tính bằng byte. Miễn là dòng này <= 8KB, nó sẽ đọc toàn bộ dòng cùng một lúc.
Doktor J

1
Tại sao đây không phải là câu trả lời tốt nhất?
GunJack 17/12/13

1
Làm thế nào để bạn xử lý lỗi với phương pháp này? Điều gì xảy ra nếu 404 được trả về hoặc kết nối bị gián đoạn hoặc hết thời gian?
Adam Swinden

67

Hãy thử sử dụng cURL

set_time_limit(0); // unlimited max execution time
$options = array(
  CURLOPT_FILE    => '/path/to/download/the/file/to.zip',
  CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
  CURLOPT_URL     => 'http://remoteserver.com/path/to/big/file.zip',
);

$ch = curl_init();
curl_setopt_array($ch, $options);
curl_exec($ch);
curl_close($ch);

Tôi không chắc nhưng tôi tin với CURLOPT_FILEtùy chọn nó ghi khi nó kéo dữ liệu, tức là. không được đệm.


2
Thông thường, điều này sẽ ổn, nhưng tôi có mã này trong một ứng dụng web, vì vậy tôi không thể chắc chắn người dùng sẽ cài đặt cURL. Tuy nhiên, tôi đã bỏ phiếu này.
xaav

@Geoff có phải là một ứng dụng web phân tán? Bởi vì nếu bạn kiểm soát việc lưu trữ thì điều đó không quan trọng đối với người dùng của bạn (cURL là một thư viện trên máy chủ của bạn).
alex

Không. Tôi không kiểm soát việc lưu trữ. Nó là một ứng dụng web phân tán mà bất cứ ai cũng có thể có.
xaav

3
Curl có thể bị mất. Nhưng hầu như tất cả các công ty lưu trữ chia sẻ đều có cài đặt CURL theo mặc định. Ý tôi là, tôi chưa thấy cái nào không.
Mangirdas Skripka

19
Từ các thử nghiệm của tôi, bạn không thể chỉ định trực tiếp cho CURLOPT_FILE một đường dẫn tệp. Nó phải là một trình xử lý tập tin. Đầu tiên, mở tệp với $fh = fopen('/path/to/download/the/file/to.zip', 'w');và đóng với fclose($fh);sau curl_close($ch);. Và thiết lậpCURLOPT_FILE => $fh
Gustavo

22

câu trả lời của prodigitalson không làm việc cho tôi. Tôi có missing fopen in CURLOPT_FILE thêm chi tiết .

Điều này làm việc cho tôi, bao gồm các url địa phương:

function downloadUrlToFile($url, $outFileName)
{   
    if(is_file($url)) {
        copy($url, $outFileName); 
    } else {
        $options = array(
          CURLOPT_FILE    => fopen($outFileName, 'w'),
          CURLOPT_TIMEOUT =>  28800, // set this to 8 hours so we dont timeout on big files
          CURLOPT_URL     => $url
        );

        $ch = curl_init();
        curl_setopt_array($ch, $options);
        curl_exec($ch);
        curl_close($ch);
    }
}

19
  1. Tạo một thư mục gọi là "tải xuống" trong máy chủ đích
  2. Lưu [mã này] vào .phptệp và chạy trong máy chủ đích

Trình tải xuống:

<html>
<form method="post">
<input name="url" size="50" />
<input name="submit" type="submit" />
</form>
<?php
    // maximum execution time in seconds
    set_time_limit (24 * 60 * 60);

    if (!isset($_POST['submit'])) die();

    // folder to save downloaded files to. must end with slash
    $destination_folder = 'downloads/';

    $url = $_POST['url'];
    $newfname = $destination_folder . basename($url);

    $file = fopen ($url, "rb");
    if ($file) {
      $newf = fopen ($newfname, "wb");

      if ($newf)
      while(!feof($file)) {
        fwrite($newf, fread($file, 1024 * 8 ), 1024 * 8 );
      }
    }

    if ($file) {
      fclose($file);
    }

    if ($newf) {
      fclose($newf);
    }
?>
</html> 

Điều này giả định rằng người dùng muốn một tập lệnh độc lập hơn là một giải pháp sẽ hoạt động trong một ứng dụng PHP hiện có và tôi tin rằng cái sau là thứ mà OP và hầu hết những người khác đang tìm kiếm. Một lời giải thích cũng sẽ hữu ích cho những người muốn hiểu cách tiếp cận.
Sean the Bean

1
Bất cứ khi nào tôi thử điều này luôn kích thước tệp được chuyển của tôi là 50816 nhưng kích thước tệp của tôi lớn hơn thế này .. 120MB .. Có ai biết tại sao lại như vậy không?
Riffaz Starr

set_time_limit (24 * 60 * 60);phải được đặt trong một vòng lặp. Nó không có hiệu lực ở đầu kịch bản.
Viktor Joras

16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);

Câu trả lời của bạn rất đơn giản và hoạt động tốt, đã giúp tôi khi cURL không nhận được tệp, điều này đã làm việc. Cảm ơn :)
Tommix 24/214

2
Bạn có thể muốn giải thích điều này thực sự làm gì.
alex

6
Điều này không giải quyết vấn đề của OP vượt quá giới hạn bộ nhớ PHP.
dùng9645

Điều này khá đơn giản và dễ hiểu. Khá hữu ích cho các trường hợp đơn giản hơn khi các tệp nhỏ hoặc môi trường là một sự phát triển cục bộ.
Valentine Shi

Bất kỳ ý tưởng cho các tập tin .xlsx? Nó đang lưu trữ tệp trống với bộ nhớ 0 byte.
Dhruv Thakkar


8

Sử dụng một phương thức đơn giản trong php copy()

copy($source_url, $local_path_with_file_name);

Lưu ý: nếu tệp đích đã tồn tại, nó sẽ bị ghi đè

Hàm sao chép PHP ()

Lưu ý: Bạn cần đặt quyền 777 cho thư mục đích. Sử dụng phương pháp này khi bạn đang tải xuống máy cục bộ của bạn.

Lưu ý đặc biệt: 777 là một quyền trong hệ thống dựa trên Unix với quyền đọc / ghi / thực thi đầy đủ cho chủ sở hữu, nhóm và mọi người. Nói chung, chúng tôi cấp quyền này cho các tài sản không cần thiết phải ẩn khỏi công khai trên máy chủ web. Ví dụ: thư mục hình ảnh.


1
Tôi sẽ không bao giờ không bao giờ thiết lập 777 như perm trên máy chủ web và tôi sẽ khởi động bất kỳ nhà phát triển web nào có ý tưởng tồi để làm điều đó. Mọi lúc mọi nơi. Hãy cẩn thận ! Bạn không thể làm điều đó ! Hãy nghĩ về an ninh. Theo quy tắc OWASP là không đủ. Có suy nghĩ tốt về những điều đơn giản vấn đề.
ThierryB

@ThierryB. Lưu ý: Tôi đã đưa ra đường dẫn cục bộ. & điều này có thể được sử dụng trong các ứng dụng nội bộ. Có đọc tốt và hiểu câu hỏi và trả lời vấn đề. Hãy suy nghĩ các kịch bản khác nhau. Và điều này không được chấp nhận / câu trả lời tốt nhất. Mỗi câu hỏi đều có câu trả lời khác nhau với ưu và nhược điểm trong đó .. Ví dụ để bạn hiểu: Ngay cả Fibonacci cũng có nhiều giải pháp duy nhất trong đó chỉ có một giải pháp tốt nhất. Những người khác sẽ được sử dụng trong các kịch bản khác nhau.
Pradeep Kumar Mitchaharan

Ok, nhưng dành thời gian để suy nghĩ về các thực tiễn tốt nhất và thực hiện chúng trong các địa điểm được bảo mật sẽ giúp bạn hiểu rõ hơn về các khái niệm bạn phải thực hiện. Có thể nếu kẻ đột nhập ở trong nhà ($) của bạn, thực hiện một số bẫy hoặc xây dựng mọi thứ theo cách tốt nhất bạn có thể khiến anh ta đau đầu;)
ThierryB

5

Tôi sử dụng để tải tập tin

function cURLcheckBasicFunctions()
{
  if( !function_exists("curl_init") &&
      !function_exists("curl_setopt") &&
      !function_exists("curl_exec") &&
      !function_exists("curl_close") ) return false;
  else return true;
}

/*
 * Returns string status information.
 * Can be changed to int or bool return types.
 */
function cURLdownload($url, $file)
{
  if( !cURLcheckBasicFunctions() ) return "UNAVAILABLE: cURL Basic Functions";
  $ch = curl_init();
  if($ch)
  {

    $fp = fopen($file, "w");
    if($fp)
    {
      if( !curl_setopt($ch, CURLOPT_URL, $url) )
      {
        fclose($fp); // to match fopen()
        curl_close($ch); // to match curl_init()
        return "FAIL: curl_setopt(CURLOPT_URL)";
      }
      if ((!ini_get('open_basedir') && !ini_get('safe_mode')) || $redirects < 1) {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_HEADER, $curlopt_header)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, $redirects > 0)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_MAXREDIRS, $redirects) ) return "FAIL: curl_setopt(CURLOPT_MAXREDIRS)";

        return curl_exec($ch);
    } else {
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        //curl_setopt($ch, CURLOPT_REFERER, 'http://domain.com/');
        if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false)) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
        if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
        if( !curl_setopt($ch, CURLOPT_HEADER, true)) return "FAIL: curl_setopt(CURLOPT_HEADER)";
        if( !curl_setopt($ch, CURLOPT_RETURNTRANSFER, true)) return "FAIL: curl_setopt(CURLOPT_RETURNTRANSFER)";
        if( !curl_setopt($ch, CURLOPT_FORBID_REUSE, false)) return "FAIL: curl_setopt(CURLOPT_FORBID_REUSE)";
        curl_setopt($ch, CURLOPT_USERAGENT, '"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.11) Gecko/20071204 Ubuntu/7.10 (gutsy) Firefox/2.0.0.11');
    }
      // if( !curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true) ) return "FAIL: curl_setopt(CURLOPT_FOLLOWLOCATION)";
      // if( !curl_setopt($ch, CURLOPT_FILE, $fp) ) return "FAIL: curl_setopt(CURLOPT_FILE)";
      // if( !curl_setopt($ch, CURLOPT_HEADER, 0) ) return "FAIL: curl_setopt(CURLOPT_HEADER)";
      if( !curl_exec($ch) ) return "FAIL: curl_exec()";
      curl_close($ch);
      fclose($fp);
      return "SUCCESS: $file [$url]";
    }
    else return "FAIL: fopen()";
  }
  else return "FAIL: curl_init()";
}

4

Giải pháp PHP 4 & 5:

readfile () sẽ không trình bày bất kỳ vấn đề bộ nhớ nào, ngay cả khi tự gửi các tệp lớn. Một URL có thể được sử dụng làm tên tệp với chức năng này nếu trình bao bọc fopen đã được bật.

http://php.net/manual/en/feft.readfile.php


1
Điều này không trả lời câu hỏi, bởi vì câu hỏi là về việc ghi trên đĩa không vào bộ đệm đầu ra.
Lorenz Meyer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.