ดาวน์โหลดไฟล์ไปยังเซิร์ฟเวอร์จาก URL


341

อันนี้ดูเหมือนง่ายมากและเป็น สิ่งที่คุณต้องทำเพื่อดาวน์โหลดไฟล์ไปยังเซิร์ฟเวอร์ของคุณคือ:

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

มีเพียงปัญหาเดียวเท่านั้น ถ้าคุณมีไฟล์ขนาดใหญ่เช่น 100mb จากนั้นคุณจะมีหน่วยความจำไม่เพียงพอและไม่สามารถดาวน์โหลดไฟล์ได้

สิ่งที่ฉันต้องการคือวิธีการเขียนไฟล์ไปยังดิสก์ขณะที่ฉันกำลังดาวน์โหลด ด้วยวิธีนี้ฉันสามารถดาวน์โหลดไฟล์ที่ใหญ่กว่าโดยไม่ต้องเจอปัญหาหน่วยความจำ


4
ที่ตั้งค่าในการกำหนดค่าเซิร์ฟเวอร์ของคุณ, PHP ไม่สามารถจริงๆได้รับรอบมันเท่าที่ฉันรู้ (ยกเว้นแก้ไข .ini โดยตรง)
เบน

คำตอบ:


494

ตั้งแต่ PHP 5.1.0 file_put_contents()สนับสนุนการเขียนทีละชิ้นโดยส่ง stream-handle เป็น$dataพารามิเตอร์:

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

จากคู่มือ:

หากdata [นั่นคืออาร์กิวเมนต์ที่สอง] เป็นแหล่งข้อมูลบัฟเฟอร์ที่เหลืออยู่ของกระแสนั้นจะถูกคัดลอกไปยังไฟล์ที่ระบุ stream_copy_to_stream()นี้จะคล้ายกับการใช้

(ขอบคุณฮาเคร)


4
นั่นคงไม่ใช่ทางเลือกแรกของฉัน หากallow_fopen_url Offตั้งไว้ใน php.ini (ความคิดที่ดีเพื่อความปลอดภัย) สคริปต์ของคุณจะไม่ทำงาน
กรุณามา

4
@idealmachine ฉันคิดว่าfile_get_contents()คงไม่ทำงานเหมือนกันถ้าเป็นเช่นนั้น (ดู OP)
alex

10
@geoff ฉันเจาะจงฉันพูดถึงฟังก์ชั่นที่คุณต้องการ สิ่งที่คุณอาจต้องการคือคนที่เขียนโค้ดให้คุณ - แต่ฉันแน่ใจว่าคุณได้เรียนรู้สิ่งที่ทำเอง นอกจากนี้ถ้าเราจะแสดงความคิดเห็นในการโต้ตอบ SO ของกันและกัน - โปรดยอมรับคำตอบเพิ่มเติม :)
alex

@alex: โปรดดูการแก้ไขอย่าลังเลที่จะรวม แจ้งให้เราทราบเมื่อฉันสามารถลบความคิดเห็นนี้ได้ที่นี่แล้ว
hakre

4
ควรใช้ค่าสถานะ 'b' ในกรณีส่วนใหญ่ด้วยfopen; ป้องกันผลกระทบต่อภาพและไฟล์ข้อความอื่น ๆ ที่ไม่ใช่ข้อความธรรมดา
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
ขอบคุณสำหรับ snippit ของคุณ แต่คุณจะสามารถอธิบายรหัสของคุณ @xaav ได้หรือไม่? ฉันไม่เก่งอย่างแน่นอนที่ php 1024 * 8 คืออะไร ขอบคุณอีกครั้ง.
vvMINOvv

@wMINOw ความยาวของเส้น
David Bélanger

2
โดยเฉพาะมันหมายถึงการอ่านสูงสุด 8KB ในเวลา (1024 ไบต์ต่อ KB * 8) เนื่องจากพารามิเตอร์เป็นไบต์ ตราบใดที่บรรทัดนั้น <= 8KB มันจะอ่านทั้งบรรทัดในครั้งเดียว
Doktor J

1
ทำไมนี่ไม่ใช่คำตอบที่ดีที่สุด?
GunJack

1
คุณจัดการข้อผิดพลาดด้วยวิธีนี้ได้อย่างไร เกิดอะไรขึ้นถ้า 404 ถูกส่งคืนหรือการเชื่อมต่อถูกขัดจังหวะหรือหมดเวลา?
Adam Swinden

67

ลองใช้ 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);

ฉันไม่แน่ใจ แต่ฉันเชื่อว่ามีCURLOPT_FILEตัวเลือกที่เขียนในขณะที่ดึงข้อมูลเช่น ไม่บัฟเฟอร์


2
โดยปกติแล้วจะใช้ได้ แต่ฉันมีรหัสนี้ในเว็บแอปดังนั้นฉันไม่แน่ใจว่าผู้ใช้จะติดตั้ง cURL อย่างไรก็ตามฉันได้ให้คะแนนนี้
xaav

@Geoff เป็นแอพพลิเคชั่นบนเว็บแบบกระจายหรือไม่ เพราะถ้าคุณควบคุมโฮสติ้งแล้วมันไม่สำคัญกับผู้ใช้ของคุณ (cURL เป็นไลบรารีบนเซิร์ฟเวอร์ของคุณ)
alex

ไม่ฉันไม่ได้ควบคุมโฮสติ้ง มันเป็นเว็บแอพแบบกระจายที่ทุกคนสามารถมีได้
xaav

3
Curl อาจหายไป แต่ บริษัท โฮสติ้งที่ใช้ร่วมกันเกือบทั้งหมดมีการติดตั้ง CURL ตามค่าเริ่มต้น ฉันหมายถึงฉันไม่ได้เห็นคนที่ไม่ได้
Mangirdas Skripka

19
จากการทดสอบของฉันคุณไม่สามารถกำหนดให้กับ CURLOPT_FILE พา ธ ไฟล์โดยตรง ต้องเป็นตัวจัดการไฟล์ ก่อนอื่นให้เปิดไฟล์ด้วย$fh = fopen('/path/to/download/the/file/to.zip', 'w');และปิดด้วยfclose($fh);หลังจากcurl_close($ch);นั้น และตั้งค่าCURLOPT_FILE => $fh
Gustavo

22

คำตอบของ prodigitals ไม่ได้ผลสำหรับฉัน ผมได้รับรายละเอียดเพิ่มเติมmissing fopen in CURLOPT_FILE

สิ่งนี้ใช้ได้สำหรับฉันรวมถึง URL ในท้องถิ่น:

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. สร้างโฟลเดอร์ชื่อ "ดาวน์โหลด" ในเซิร์ฟเวอร์ปลายทาง
  2. บันทึก [รหัสนี้] ลงใน.phpไฟล์และเรียกใช้ในเซิร์ฟเวอร์ปลายทาง

ตัวดาวน์โหลด:

<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> 

สิ่งนี้ถือว่าผู้ใช้ต้องการสคริปต์แบบสแตนด์อโลนแทนที่จะเป็นโซลูชันที่จะทำงานภายในแอปพลิเคชัน PHP ที่มีอยู่และฉันเชื่อว่าสิ่งหลังคือสิ่งที่ OP และคนอื่น ๆ มองหา คำอธิบายจะเป็นประโยชน์สำหรับผู้ที่ต้องการเข้าใจวิธีการ
Sean the Bean

1
เมื่อใดก็ตามที่ฉันลองใช้ขนาดไฟล์ที่ถ่ายโอนของฉันคือ 50816 แต่ขนาดไฟล์ของฉันใหญ่กว่านี้ .. 120MB .. คุณคิดว่าทำไมถึงเป็นเช่นนั้น
Riffaz Starr

set_time_limit (24 * 60 * 60);จะต้องใส่ในวง ไม่มีผลในตอนต้นของสคริปต์
Viktor Joras

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

คำตอบของคุณนั้นเรียบง่ายและทำงานได้ดีช่วยฉันในที่ที่ cURL ไม่สามารถรับไฟล์ได้ ขอบคุณ :)
Tommix

2
คุณอาจต้องการอธิบายสิ่งที่เกิดขึ้นจริง
alex

6
สิ่งนี้ไม่ได้แก้ไขปัญหา OP ของเกินขีด จำกัด หน่วยความจำ PHP
user9645

มันค่อนข้างเรียบง่ายและตรงไปตรงมา ค่อนข้างมีประโยชน์สำหรับกรณีที่ง่ายกว่าซึ่งไฟล์มีขนาดเล็กหรือสภาพแวดล้อมเป็นการพัฒนาท้องถิ่น
Valentine Shi Shi

แนวคิดสำหรับไฟล์. xlsx หรือไม่ กำลังจัดเก็บไฟล์ว่างด้วยหน่วยความจำ 0 ไบต์
Dhruv Thakkar

9

มี 3 วิธี:

  1. file_get_contents และ file_put_contents
  2. CURL
  3. fopen

คุณสามารถหาตัวอย่างจากที่นี่


8

ใช้วิธีการง่ายๆใน PHP copy()

copy($source_url, $local_path_with_file_name);

หมายเหตุ: หากไฟล์ปลายทางมีอยู่แล้วไฟล์นั้นจะถูกเขียนทับ

PHP copy () ฟังก์ชั่น

หมายเหตุ: คุณต้องตั้งค่าการอนุญาต 777 สำหรับโฟลเดอร์ปลายทาง ใช้วิธีนี้เมื่อคุณดาวน์โหลดลงในเครื่องของคุณ

หมายเหตุพิเศษ: 777 ได้รับอนุญาตในระบบที่ใช้ Unix พร้อมสิทธิ์การอ่าน / เขียน / ดำเนินการเต็มรูปแบบให้กับเจ้าของกลุ่มและทุกคน โดยทั่วไปเราให้สิทธิ์นี้กับเนื้อหาที่ไม่จำเป็นต้องซ่อนจากสาธารณะบนเว็บเซิร์ฟเวอร์ ตัวอย่าง: โฟลเดอร์รูปภาพ


1
ฉันจะไม่เคยไม่เคยตั้งค่า 777 เป็น perms ในเว็บเซิร์ฟเวอร์และฉันจะเริ่มต้นนักพัฒนาเว็บที่มีความคิดที่ดีที่จะทำเช่นนั้น ทุกครั้งทุกที่ ระวัง ! คุณไม่สามารถทำได้! คิดถึงความปลอดภัย การติดตามกฎ OWASP ไม่เพียงพอ การมีความคิดที่ดีเกี่ยวกับเรื่องง่าย ๆ เป็นเรื่องสำคัญ
ThierryB

@ThierryB หมายเหตุ: ฉันได้รับเส้นทางท้องถิ่น และสิ่งนี้สามารถใช้ในแอปพลิเคชันภายใน มีความเข้าใจในคำถามและคำตอบที่ดี คิดว่าสถานการณ์ต่าง ๆ และนี่ไม่เป็นที่ยอมรับ / คำตอบที่ดีที่สุด คำถามทุกข้อมีคำตอบที่แตกต่างกันด้วยข้อดีข้อเสียตัวอย่างสำหรับคุณที่จะเข้าใจ: แม้แต่ฟีโบนัชชีก็มีวิธีแก้ปัญหาที่ไม่เหมือนใครซึ่งจะดีที่สุดเพียงข้อเดียวเท่านั้น คนอื่น ๆ จะถูกนำมาใช้ในสถานการณ์ต่าง ๆ
Pradeep Kumar Prabaharan

ตกลง แต่ต้องใช้เวลาคิดเกี่ยวกับแนวทางปฏิบัติที่ดีที่สุดและนำไปปฏิบัติในสถานที่ที่ปลอดภัยจะทำให้คุณเข้าใจแนวคิดที่คุณต้องนำไปปฏิบัติได้ดีขึ้น บางทีถ้าผู้บุกรุกอยู่ในบ้าน ($) ของคุณทำกับดักหรือสร้างสิ่งต่าง ๆ วิธีที่ดีที่สุดที่คุณจะทำให้เขาปวดหัว;)
ThierryB

5

ฉันใช้สิ่งนี้เพื่อดาวน์โหลดไฟล์

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

โซลูชัน PHP 4 และ 5:

readfile ()จะไม่นำเสนอปัญหาหน่วยความจำใด ๆ แม้ว่าจะส่งไฟล์ขนาดใหญ่ด้วยตัวเอง URL สามารถใช้เป็นชื่อไฟล์ที่มีฟังก์ชั่นนี้ได้หากมีการเปิดใช้งาน wrapper fopen

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


1
สิ่งนี้ไม่ตอบคำถามเนื่องจากคำถามเกี่ยวกับการเขียนบนดิสก์ไม่ใช่ไปยังบัฟเฟอร์เอาต์พุต
ลอเรนเมเยอร์
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.