Cara memaksa mengunduh file dengan PHP

125

Saya ingin meminta file untuk diunduh saat pengguna mengunjungi halaman web dengan PHP. Saya pikir itu ada hubungannya dengan file_get_contents, tetapi saya tidak yakin bagaimana menjalankannya.

$url = "http://example.com/go.exe";

Setelah mengunduh file header(location)dengannya tidak mengarahkan ke halaman lain. Itu berhenti begitu saja.

john
sumber
kemungkinan duplikat pengunduhan halaman paksa di php
hakre
kemungkinan duplikat dari Memaksa untuk mengunduh file menggunakan PHP
pengguna

Jawaban:

234

Baca dokumen tentang readfile fungsi PHP bawaan

$file_url = 'http://www.myremoteserver.com/file.exe';
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary"); 
header("Content-disposition: attachment; filename=\"" . basename($file_url) . "\""); 
readfile($file_url); 

Pastikan juga untuk menambahkan jenis konten yang tepat berdasarkan aplikasi file / zip, aplikasi / pdf, dll. - tetapi hanya jika Anda tidak ingin memicu dialog simpan-sebagai.

Penggali Lubang
sumber
6
Mengapa itu diperlukan?
john
Maksud saya jika itu selain file .exe daripada hanya. Lain ini seharusnya berfungsi dengan baik.
Pit Digger
7
Jangan lupa untuk menyiram ;-) ob_clean (); menyiram(); / * sebelum * / readfile ($ file_url);
Abu
Jadi jika Anda memiliki file besar 10GB php mencoba memuat seluruh file itu?
GDY
exit () harus dipanggil di akhir untuk menghindari potensi masalah (berbicara dari pengalaman :-)
ykay mengatakan Reinstate Monica
42
<?php
$file = "http://example.com/go.exe"; 

header("Content-Description: File Transfer"); 
header("Content-Type: application/octet-stream"); 
header("Content-Disposition: attachment; filename=\"". basename($file) ."\""); 

readfile ($file);
exit(); 
?>

Atau, jika file tidak dapat dibuka dengan browser, Anda dapat menggunakan Locationheader:

<?php header("Location: http://example.com/go.exe"); ?>
Marek Sebera
sumber
1
nama file di header Anda tidak boleh $file(yang berisi bagian http) tetapi nama file yang valid.
Fabio
1
Tidak ada jenis media aplikasi / unduh paksa ; gunakan application / octet-stream sebagai gantinya.
Gumbo
bekerja sangat bagus! Tetapi menambahkan 'ke nama file yang disimpan. Silakan gunakan: filename = ". Basename ($ file));
harry4516
@ harry4516 dimodifikasi menurut temuan Anda
Marek Sebera
Tidak bekerja sayang dalam kasus gambar png untuk saya. Terima kasih.
Kamlesh
40
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\""); 
echo readfile($url);

benar

atau lebih baik untuk jenis file exe

header("Location: $url");
asal
sumber
@Fabio: Akan menambahkan lebih banyak detials
genesis
Itu sangat mudah. Berhasil. Untuk apa file-Get_contents itu? Hanya penasaran. Terima kasih.
john
untuk mengunduhnya ke server Anda
kejadian
10
Hapus saja 'echo' sebelum 'readfile ()' karena nilai yang dikembalikan ditetapkan sebagai "Mengembalikan jumlah byte yang dibaca dari file. Jika terjadi kesalahan, FALSE dikembalikan dan kecuali fungsi itu dipanggil sebagai @readfile (), pesan kesalahan dicetak. ". Jadi Anda akan berakhir dengan konten file + bilangan bulat di akhir konten.
Mladen B.
22

Tampilkan file Anda terlebih dahulu dan atur nilainya menjadi url.

index.php

<a href="download.php?download='.$row['file'].'" title="Download File">

download.php

<?php
/*db connectors*/
include('dbconfig.php');

/*function to set your files*/
function output_file($file, $name, $mime_type='')
{
    if(!is_readable($file)) die('File not found or inaccessible!');
    $size = filesize($file);
    $name = rawurldecode($name);
    $known_mime_types=array(
        "htm" => "text/html",
        "exe" => "application/octet-stream",
        "zip" => "application/zip",
        "doc" => "application/msword",
        "jpg" => "image/jpg",
        "php" => "text/plain",
        "xls" => "application/vnd.ms-excel",
        "ppt" => "application/vnd.ms-powerpoint",
        "gif" => "image/gif",
        "pdf" => "application/pdf",
        "txt" => "text/plain",
        "html"=> "text/html",
        "png" => "image/png",
        "jpeg"=> "image/jpg"
    );

    if($mime_type==''){
        $file_extension = strtolower(substr(strrchr($file,"."),1));
        if(array_key_exists($file_extension, $known_mime_types)){
            $mime_type=$known_mime_types[$file_extension];
        } else {
            $mime_type="application/force-download";
        };
    };
    @ob_end_clean();
    if(ini_get('zlib.output_compression'))
    ini_set('zlib.output_compression', 'Off');
    header('Content-Type: ' . $mime_type);
    header('Content-Disposition: attachment; filename="'.$name.'"');
    header("Content-Transfer-Encoding: binary");
    header('Accept-Ranges: bytes');

    if(isset($_SERVER['HTTP_RANGE']))
    {
        list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
        list($range) = explode(",",$range,2);
        list($range, $range_end) = explode("-", $range);
        $range=intval($range);
        if(!$range_end) {
            $range_end=$size-1;
        } else {
            $range_end=intval($range_end);
        }

        $new_length = $range_end-$range+1;
        header("HTTP/1.1 206 Partial Content");
        header("Content-Length: $new_length");
        header("Content-Range: bytes $range-$range_end/$size");
    } else {
        $new_length=$size;
        header("Content-Length: ".$size);
    }

    $chunksize = 1*(1024*1024);
    $bytes_send = 0;
    if ($file = fopen($file, 'r'))
    {
        if(isset($_SERVER['HTTP_RANGE']))
        fseek($file, $range);

        while(!feof($file) &&
            (!connection_aborted()) &&
            ($bytes_send<$new_length)
        )
        {
            $buffer = fread($file, $chunksize);
            echo($buffer);
            flush();
            $bytes_send += strlen($buffer);
        }
        fclose($file);
    } else
        die('Error - can not open file.');
    die();
}
set_time_limit(0);

/*set your folder*/
$file_path='uploads/'."your file";

/*output must be folder/yourfile*/

output_file($file_path, ''."your file".'', $row['type']);

/*back to index.php while downloading*/
header('Location:index.php');
?>
jundell agbo
sumber
2
Darimana kamu mendapatkan ini? Tampaknya kuat
Axel A. García
@ jundell-agbo menarik dan untuk file crt?
fralbo
Ini juga berfungsi untuk file yang sangat besar. Solusi yang bagus. Tapi` $ chunksize = 1 * (1024 * 1024); `lambat. Saya mencoba dengan beberapa nilai dan melihat bahwa `$ chunksize = 8 * (1024 * 1024);` menggunakan semua bandwidth yang tersedia.
Lingga
16

Jika Anda harus mengunduh file dengan ukuran lebih besar dari batas memori yang diizinkan ( memory_limitpengaturan ini), yang akan menyebabkan PHP Fatal error: Allowed memory size of 5242880 bytes exhaustedkesalahan, Anda dapat melakukan ini:

// File to download.
$file = '/path/to/file';

// Maximum size of chunks (in bytes).
$maxRead = 1 * 1024 * 1024; // 1MB

// Give a nice name to your download.
$fileName = 'download_file.txt';

// Open a file in read mode.
$fh = fopen($file, 'r');

// These headers will force download on browser,
// and set the custom file name for the download, respectively.
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $fileName . '"');

// Run this until we have read the whole file.
// feof (eof means "end of file") returns `true` when the handler
// has reached the end of file.
while (!feof($fh)) {
    // Read and output the next chunk.
    echo fread($fh, $maxRead);

    // Flush the output buffer to free memory.
    ob_flush();
}

// Exit to make sure not to output anything else.
exit;
Parziphal
sumber
Saya baru saja mencoba ini dan itu mematikan server saya. Menulis sejumlah besar kesalahan ke log saya.
Daniel Williams
Akan berguna untuk mengetahui tentang apa log itu.
Parziphal
Ya maaf, file log menjadi sangat besar sehingga saya harus membuangnya begitu saja, jadi saya tidak bisa melihatnya.
Daniel Williams
Saya harus menyiapkan sesuatu seperti ini di server yang aksesnya sangat terbatas. Skrip unduhan saya terus mengarah ke halaman beranda dan saya tidak tahu mengapa. Sekarang saya tahu masalahnya adalah kesalahan memori dan kode ini menyelesaikannya.
Gavin
Jika Anda baru saja menggunakan ob_flush()mungkin ada kesalahan: ob_flush (): gagal membersihkan buffer. Tidak ada buffer untuk disiram . membungkusnya dengan if (ob_get_level() > 0) {ob_flush();}(Referensi stackoverflow.com/a/9182133/128761 )
vee
11

Modifikasi jawaban yang diterima di atas, yang juga mendeteksi jenis MIME dalam runtime:

$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $path));

$finfo = finfo_open(FILEINFO_MIME_ENCODING);
header('Content-Transfer-Encoding: '.finfo_file($finfo, $path)); 

header('Content-disposition: attachment; filename="'.basename($path).'"'); 
readfile($path); // do the double-download-dance (dirty but worky)
Jure Sah
sumber
4

Kode berikut adalah cara yang benar untuk mengimplementasikan layanan unduhan di php seperti yang dijelaskan dalam tutorial berikut

header('Content-Type: application/zip');
header("Content-Disposition: attachment; filename=\"$file_name\"");
set_time_limit(0);
$file = @fopen($filePath, "rb");
while(!feof($file)) {
    print(@fread($file, 1024*8));
    ob_flush();
    flush();
}
Grigor Nazaryan
sumber
1
Anda memiliki dua variabel dalam kode ini: file_namedan filePath. Praktik pengkodean yang tidak terlalu baik. Apalagi tanpa penjelasan. Menautkan ke tutorial eksternal tidak membantu.
Khom Nazid
2

coba ini:

header('Content-type: audio/mp3'); 
header('Content-disposition: attachment; 
filename=“'.$trackname'”');                             
readfile('folder name /'.$trackname);          
exit();
nqobile
sumber
1

Anda juga dapat melakukan streaming unduhan yang akan menghabiskan lebih sedikit sumber daya. contoh:

$readableStream = fopen('test.zip', 'rb');
$writableStream = fopen('php://output', 'wb');

header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="test.zip"');
stream_copy_to_stream($readableStream, $writableStream);
ob_flush();
flush();

Dalam contoh di atas, saya mengunduh test.zip (yang sebenarnya adalah zip studio android di mesin lokal saya). php: // output adalah aliran hanya-tulis (umumnya digunakan oleh echo atau print). setelah itu, Anda hanya perlu menyetel header yang diperlukan dan memanggil stream_copy_to_stream (sumber, tujuan). metode stream_copy_to_stream () bertindak sebagai pipa yang mengambil input dari aliran sumber (aliran baca) dan menyalurkannya ke aliran tujuan (aliran tulis) dan juga menghindari masalah kehabisan memori yang diizinkan sehingga Anda benar-benar dapat mengunduh file yang lebih besar daripada PHP Anda memory_limit.

Saud Qureshi
sumber
Apa yang Anda maksud dengan unduhan itu streaming?
Parsa Yazdani
1
@ParsaYazdani Artinya data yang diunduh akan dipotong-potong. Silakan lihat konsep streaming untuk lebih jelasnya. Catatan: ini bukan satu-satunya untuk melakukan streaming (potongan) mengunduh file dalam PHP. Tapi itu pasti salah satu metode termudah.
Saud Qureshi
0

Jawaban di atas saya berhasil. Tapi, saya ingin menyumbangkan metode tentang bagaimana melakukannya menggunakan GET

di halaman html / php Anda

$File = 'some/dir/file.jpg';
<a href="<?php echo '../sumdir/download.php?f='.$File; ?>" target="_blank">Download</a>

dan download.phpberisi

$file = $_GET['f']; 

header("Expires: 0");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");

$ext = pathinfo($file, PATHINFO_EXTENSION);
$basename = pathinfo($file, PATHINFO_BASENAME);

header("Content-type: application/".$ext);
header('Content-length: '.filesize($file));
header("Content-Disposition: attachment; filename=\"$basename\"");
ob_clean(); 
flush();
readfile($file);
exit;

ini harus bekerja pada semua jenis file. ini tidak diuji menggunakan POST, tetapi dapat berhasil.

Mo Chan
sumber