Unduh File ke server dari URL

341

Nah, yang ini sepertinya cukup sederhana, dan memang begitu. Yang harus Anda lakukan untuk mengunduh file ke server Anda adalah:

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

Hanya ada satu masalah. Bagaimana jika Anda memiliki file besar, seperti 100mb. Kemudian, Anda akan kehabisan memori, dan tidak dapat mengunduh file.

Apa yang saya inginkan adalah cara untuk menulis file ke disk saat saya mengunduhnya. Dengan begitu, saya bisa mengunduh file yang lebih besar, tanpa mengalami masalah memori.

xaav
sumber
4
Itu diatur dalam konfigurasi server Anda, PHP tidak bisa benar-benar menyiasatinya sejauh yang saya tahu (kecuali untuk mengedit .ini langsung)
Ben

Jawaban:

494

Sejak PHP 5.1.0, file_put_contents()mendukung penulisan sepotong demi sepotong dengan memberikan stream-handle sebagai $dataparameter:

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

Dari manual:

Jika data [itu adalah argumen kedua] adalah sumber daya aliran, buffer yang tersisa dari aliran itu akan disalin ke file yang ditentukan. Ini mirip dengan menggunakan stream_copy_to_stream().

(Terima kasih Hakre .)

alex
sumber
4
Itu tidak akan menjadi pilihan pertama saya. Jika allow_fopen_url Offdiatur dalam php.ini (ide bagus untuk keamanan), skrip Anda akan rusak.
PleaseStand
4
@idealmachine Saya pikir file_get_contents()tidak akan berhasil jika itu terjadi (lihat OP).
alex
10
@geoff Saya spesifik, saya menyebutkan fungsi yang Anda inginkan. Apa yang Anda inginkan adalah seseorang menulis kode untuk Anda - tetapi saya yakin Anda telah mempelajari sesuatu sendiri. Juga, jika kita akan mengomentari interaksi SO masing-masing - harap terima beberapa jawaban lagi :)
alex
@alex: Silakan lihat hasil edit, silakan bergabung. beri tahu saya kapan saya dapat menghapus komentar ini di sini.
hakre
4
Bendera 'b' juga harus digunakan dalam banyak kasus dengan fopen; mencegah efek buruk pada gambar dan file teks biasa lainnya.
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);
    }
}
xaav
sumber
1
terima kasih atas cuplikan Anda, tetapi apakah Anda dapat menjelaskan kode Anda @xaav? Saya tidak terlalu pintar di php. Untuk apa 1024 * 8? Terima kasih lagi.
vvMINOvv
@wMINOw Panjangnya garis.
David Bélanger
2
Secara khusus, ini berarti membaca hingga 8KB dalam satu waktu (1024 byte per KB * 8) karena parameternya dalam byte. Selama garisnya <= 8KB, itu akan membaca seluruh baris sekaligus.
Doktor J
1
Mengapa ini bukan jawaban terbaik?
GunJack
1
Bagaimana Anda menangani kesalahan dengan pendekatan ini? Bagaimana jika 404 dikembalikan atau koneksi terputus atau mati?
Adam Swinden
67

Coba gunakan 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);

Saya tidak yakin tapi saya percaya dengan CURLOPT_FILEopsi yang ditulisnya saat menarik data, yaitu. tidak buffered.

prodigitalson
sumber
2
Biasanya, ini akan baik-baik saja, tetapi saya memiliki kode ini di aplikasi web, jadi saya tidak yakin pengguna akan menginstal CURL. Namun, saya memberikan suara ini.
xaav
@ Geoff apakah ini aplikasi web terdistribusi? Karena jika Anda mengontrol hosting, maka itu tidak masalah tentang pengguna Anda (cURL adalah perpustakaan di server Anda).
alex
Tidak. Saya tidak mengontrol hosting. Ini adalah aplikasi web terdistribusi yang dapat dimiliki siapa pun.
xaav
3
Keriting mungkin hilang. Tetapi hampir semua perusahaan hosting bersama memiliki CURL diinstal secara default. Maksudku, aku belum melihat yang tidak.
Mangirdas Skripka
19
Mulai dari pengujian saya, Anda tidak dapat menetapkan jalur file secara langsung ke CURLOPT_FILE. Itu harus menjadi penangan file. Pertama, buka file dengan $fh = fopen('/path/to/download/the/file/to.zip', 'w');dan tutup fclose($fh);setelah curl_close($ch);. Dan mengaturCURLOPT_FILE => $fh
Gustavo
22

Jawaban prodigitalson tidak berhasil untuk saya. Saya mendapat missing fopen in CURLOPT_FILE lebih banyak detail .

Ini berfungsi untuk saya, termasuk url lokal:

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);
    }
}
Kamil Kiełczewski
sumber
19
  1. Buat folder bernama "unduhan" di server tujuan
  2. Simpan [kode ini] ke dalam .phpfile dan jalankan di server tujuan

Pengunduh:

<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> 
stra8edge
sumber
Ini mengasumsikan pengguna menginginkan skrip mandiri daripada solusi yang akan bekerja dalam aplikasi PHP yang ada, dan saya percaya yang terakhir adalah apa yang OP dan kebanyakan orang cari. Penjelasan juga akan bermanfaat bagi orang yang ingin memahami pendekatan tersebut.
Sean the Bean
1
setiap kali saya mencoba ini selalu ukuran file yang saya transfer 50816 tetapi ukuran file saya lebih besar dari ini .. 120MB .. Tahu kenapa ini?
Riffaz Starr
set_time_limit (24 * 60 * 60);harus dimasukkan ke dalam lingkaran. Tidak ada efek di awal naskah.
Viktor Joras
16
set_time_limit(0); 
$file = file_get_contents('path of your file');
file_put_contents('file.ext', $file);
Dimmy
sumber
jawaban Anda sangat sederhana dan berfungsi dengan baik, membantu saya ketika cURL gagal mendapatkan file, ini berhasil. Terima kasih :)
Tommix
2
Anda mungkin ingin menjelaskan apa ini sebenarnya.
alex
6
Ini tidak mengatasi masalah OP melebihi batas memori PHP.
user9645
Ini sangat sederhana dan mudah. Cukup berguna untuk kasus yang lebih sederhana di mana file berukuran kecil atau lingkungannya adalah pengembangan lokal.
Valentine Shi
ada ide untuk file .xlsx? Ini menyimpan file kosong dengan memori 0 byte.
Dhruv Thakkar
9

Ada 3 cara:

  1. file_get_contents dan file_put_contents
  2. IKAL
  3. fopen

Anda dapat menemukan contoh dari sini .

Hoan Huynh
sumber
8

Gunakan metode sederhana dalam php copy()

copy($source_url, $local_path_with_file_name);

Catatan: jika file tujuan sudah ada, itu akan ditimpa

Salin PHP () Fungsi

Catatan: Anda harus mengatur izin 777 untuk folder tujuan. Gunakan metode ini saat Anda mengunduh ke mesin lokal Anda.

Catatan Khusus: 777 adalah izin dalam sistem berbasis Unix dengan izin baca / tulis / eksekusi penuh untuk pemilik, grup, dan semua orang. Secara umum kami memberikan izin ini untuk aset yang tidak perlu disembunyikan dari publik di server web. Contoh: folder gambar.

Pradeep Kumar Prabaharan
sumber
1
Saya tidak akan pernah menetapkan 777 sebagai perm di server web, dan saya akan memulai pengembang web mana pun yang memiliki ide buruk untuk melakukan itu. Setiap saat, di mana saja. Hati-hati ! Kamu tidak bisa melakukan itu ! Pikirkan tentang keamanan. Mengikuti aturan OWASP tidak cukup. Memiliki pemikiran yang baik tentang hal-hal sederhana itu penting.
ThierryB
@ThierryB. Catatan: Saya telah memberikan jalur lokal. & Ini dapat digunakan dalam aplikasi internal. Memiliki kemampuan membaca dan memahami pertanyaan dan jawaban yang baik. Pikirkan skenario yang berbeda. Dan ini tidak diterima / jawaban terbaik. Setiap pertanyaan memiliki jawaban berbeda dengan pro & kontra di dalamnya. Contoh untuk Anda pahami: Bahkan Fibonacci memiliki beberapa solusi unik di mana hanya satu yang akan menjadi yang terbaik. Yang lain akan digunakan dalam skenario yang berbeda.
Pradeep Kumar Prabaharan
Oke, tetapi meluangkan waktu untuk memikirkan praktik terbaik dan menerapkannya di dalam tempat yang aman akan memberi Anda pemahaman yang lebih baik tentang konsep yang harus Anda terapkan. Mungkin jika seorang penyusup ada di dalam rumah ($) Anda, melakukan beberapa jebakan atau membangun hal-hal sebaik mungkin akan membuatnya sakit kepala;)
ThierryB
5

Saya menggunakan ini untuk mengunduh file

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()";
}
Ho Vng Tgtt
sumber
4

Solusi PHP 4 & 5:

readfile () tidak akan menyajikan masalah memori, bahkan saat mengirim file besar, sendiri. URL dapat digunakan sebagai nama file dengan fungsi ini jika pembungkus fopen telah diaktifkan.

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

Eric Leroy
sumber
1
Ini tidak menjawab pertanyaan, karena pertanyaannya adalah tentang menulis di disk bukan ke buffer output.
Lorenz Meyer