วิธีบังคับดาวน์โหลดไฟล์ด้วย PHP


125

ฉันต้องการต้องการให้ดาวน์โหลดไฟล์เมื่อผู้ใช้เยี่ยมชมหน้าเว็บด้วย PHP ฉันคิดว่ามันมีส่วนเกี่ยวข้องfile_get_contentsแต่ฉันไม่แน่ใจว่าจะดำเนินการอย่างไร

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

หลังจากดาวน์โหลดไฟล์โดยheader(location)ไม่มีการเปลี่ยนเส้นทางไปยังหน้าอื่น มันก็หยุด


อาจซ้ำกันได้ของการบังคับให้ดาวน์โหลดหน้าใน php
hakre

คำตอบ:


234

อ่านเอกสารเกี่ยวกับฟังก์ชั่น PHP readfile

$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); 

ตรวจสอบให้แน่ใจว่าได้เพิ่มประเภทเนื้อหาที่เหมาะสมตามแอปพลิเคชันไฟล์ / zip, application / pdf ฯลฯ - แต่ถ้าคุณไม่ต้องการเรียกใช้กล่องโต้ตอบบันทึกเป็น


6
เหตุใดจึงต้องมี
ห์น

ฉันหมายถึงถ้าเป็นไฟล์อื่นที่ไม่ใช่. exe เท่านั้นถ้าอย่างนั้นมันก็ใช้ได้ดี
Pit Digger

7
อย่าลืมล้าง ;-) ob_clean (); ล้าง (); / * ก่อน * / readfile ($ file_url);
เถ้า

ดังนั้นหากคุณมีไฟล์ขนาดใหญ่ 10GB php พยายามโหลดทั้งไฟล์?
GDY

ควรเรียก exit () ในตอนท้ายเพื่อหลีกเลี่ยงปัญหาที่อาจเกิดขึ้น (พูดจากประสบการณ์ :-)
ykay พูดว่า 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(); 
?>

หรือเมื่อไฟล์ไม่สามารถเปิดได้ด้วยเบราว์เซอร์คุณสามารถใช้Locationส่วนหัว:

<?php header("Location: http://example.com/go.exe"); ?>

1
ชื่อไฟล์ในส่วนหัวของคุณไม่ควรเป็น$file(ซึ่งมีส่วน http) แต่เป็นชื่อไฟล์ที่ถูกต้อง
Fabio

1
ไม่มีประเภทสื่อแอปพลิเคชัน / บังคับให้ดาวน์โหลด ใช้application / octet-streamแทน
Gumbo

ทำงานได้ดีมาก! แต่เพิ่ม 'ให้กับชื่อไฟล์ที่เก็บไว้ กรุณาใช้: filename = ". basename ($ file));
harry4516

@ harry4516 แก้ไขตามการค้นหาของคุณ
Marek Sebera

ไม่ทำงานที่รักในกรณีของภาพ png สำหรับฉัน ขอบคุณ
Kamlesh

40
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"file.exe\""); 
echo readfile($url);

ถูกต้อง

หรือดีกว่าสำหรับไฟล์ประเภทexe

header("Location: $url");

@Fabio: จะเพิ่มรายละเอียดเพิ่มเติม
กำเนิด

มันง่ายมาก มันได้ผล file-Get_contents คืออะไร? แค่สงสัย. ขอบคุณ
ห์น

เพื่อดาวน์โหลดไปยังเซิร์ฟเวอร์ของคุณ
กำเนิด

10
เพียงแค่ลบ 'echo' ก่อน 'readfile ()' เนื่องจากค่าส่งคืนถูกระบุเป็น "ส่งคืนจำนวนไบต์ที่อ่านจากไฟล์หากเกิดข้อผิดพลาด FALSE จะถูกส่งกลับและเว้นแต่ฟังก์ชันจะถูกเรียกเป็น @readfile () มีการพิมพ์ข้อความแสดงข้อผิดพลาด ". ดังนั้นคุณจะได้รับเนื้อหาของไฟล์ + หมายเลขจำนวนเต็มในตอนท้ายของเนื้อหา
Mladen B.

22

แสดงไฟล์ของคุณก่อนและตั้งค่าเป็น 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');
?>

2
คุณได้สิ่งนี้มาจากไหน? ดูเหมือนทรงพลัง
Axel

@ jundell-agbo น่าสนใจและสำหรับไฟล์ crt หรือไม่?
fralbo

สิ่งนี้ใช้ได้กับไฟล์ขนาดใหญ่มากเช่นกัน ทางออกที่ดี แต่` $ chunksize = 1 * (1024 * 1024); `ช้า ฉันลองใช้หลายค่าและสังเกตว่า `$ chunksize = 8 * (1024 * 1024);` กำลังใช้แบนด์วิดท์ที่มีอยู่ทั้งหมด
Linga

16

ในกรณีที่คุณต้องดาวน์โหลดไฟล์ที่มีขนาดใหญ่กว่าขีด จำกัด หน่วยความจำที่อนุญาต ( memory_limitการตั้งค่า ini) ซึ่งจะทำให้เกิดPHP Fatal error: Allowed memory size of 5242880 bytes exhaustedข้อผิดพลาดคุณสามารถทำได้:

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

ฉันเพิ่งลองสิ่งนี้และมันฆ่าเซิร์ฟเวอร์ของฉัน เขียนข้อผิดพลาดจำนวนมากในบันทึกของฉัน
Daniel Williams

มันจะมีประโยชน์ถ้ารู้ว่าท่อนไม้เกี่ยวกับอะไร
Parziphal

ขออภัยไฟล์บันทึกมีขนาดใหญ่มากฉันต้องทำให้มันแตกดังนั้นฉันจึงไม่ได้เห็น
Daniel Williams

ฉันต้องตั้งค่าบางอย่างเช่นนี้บนเซิร์ฟเวอร์ที่ฉันเข้าถึงได้ จำกัด มาก สคริปต์ดาวน์โหลดของฉันยังคงเปลี่ยนเส้นทางไปยังโฮมเพจและฉันไม่สามารถหาสาเหตุได้ ตอนนี้ฉันรู้แล้วว่าปัญหาคือข้อผิดพลาดของหน่วยความจำและรหัสนี้แก้ไขได้
Gavin

หากคุณเพิ่งใช้ob_flush()อาจมีข้อผิดพลาด: ob_flush (): ล้มเหลวในการล้างบัฟเฟอร์ ไม่มีบัฟเฟอร์เพื่อล้าง ล้อมรอบด้วยif (ob_get_level() > 0) {ob_flush();}(อ้างอิงstackoverflow.com/a/9182133/128761 )
vee

11

การแก้ไขคำตอบที่ยอมรับข้างต้นซึ่งตรวจพบประเภท MIME ในรันไทม์ด้วย:

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

4

โค้ดต่อไปนี้เป็นวิธีที่ถูกต้องในการใช้บริการดาวน์โหลดใน php ตามที่อธิบายไว้ในบทช่วยสอนต่อไปนี้

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();
}

1
คุณมีตัวแปรสองตัวในรหัสนี้: file_nameและfilePath. ฝึกเขียนโค้ดไม่ดีนัก โดยเฉพาะอย่างยิ่งไม่มีคำอธิบาย การลิงก์ไปยังบทแนะนำภายนอกไม่เป็นประโยชน์
Khom Nazid

2

ลองสิ่งนี้:

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

2

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

นั่นคือทั้งหมดที่คุณต้องการ "Monkey.gif" เปลี่ยนเป็นชื่อไฟล์ของคุณ หากคุณต้องการดาวน์โหลดจากเซิร์ฟเวอร์อื่น "monkey.gif" ให้เปลี่ยนเป็น " http://www.exsample.com/go.exe "


1
ยินดีต้อนรับสู่ StackOverflow ขอแนะนำให้ใส่รหัสโดยตรงแทนที่จะเพิ่มลิงก์เพื่อหลีกเลี่ยงปัญหาการเชื่อมโยง
Stranger

1

คุณสามารถสตรีมการดาวน์โหลดได้เช่นกันซึ่งจะใช้ทรัพยากรน้อยลงอย่างมาก ตัวอย่าง:

$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();

ในตัวอย่างข้างต้นฉันกำลังดาวน์โหลด test.zip (ซึ่งจริงๆแล้วคือ android studio zip ในเครื่องของฉัน) php: // output เป็นสตรีมแบบเขียนอย่างเดียว (โดยทั่วไปใช้โดย echo หรือ print) หลังจากนั้นคุณก็ต้องตั้งค่าส่วนหัวที่ต้องการและเรียก stream_copy_to_stream (ต้นทางปลายทาง) stream_copy_to_stream () เมธอดทำหน้าที่เป็นไพพ์ที่รับอินพุตจากสตรีมต้นทาง (อ่านสตรีม) และส่งต่อไปยังสตรีมปลายทาง (สตรีมเขียน) และยังหลีกเลี่ยงปัญหาหน่วยความจำที่อนุญาตหมดเพื่อให้คุณสามารถดาวน์โหลดไฟล์ที่ใหญ่กว่าได้ กว่า memory_limitPHP


คุณหมายความว่าอย่างไรกับการสตรีมการดาวน์โหลด
Parsa Yazdani

1
@ParsaYazdani หมายความว่าข้อมูลที่ดาวน์โหลดจะถูกจัดเรียงเป็นกลุ่ม โปรดดูแนวคิดของการสตรีมสำหรับรายละเอียดเพิ่มเติม หมายเหตุ: นี่ไม่ใช่เพียงการสตรีม (ก้อน) ดาวน์โหลดไฟล์ใน PHP แต่ก็เป็นหนึ่งในวิธีที่ง่ายที่สุด
Saud Qureshi

0

คำตอบข้างต้นใช้ได้ผล แต่ฉันต้องการให้ข้อมูลเกี่ยวกับวิธีดำเนินการโดยใช้ GET

บนหน้า html / php ของคุณ

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

และdownload.phpประกอบด้วย

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

สิ่งนี้ควรใช้ได้กับไฟล์ทุกประเภท ไม่ได้ทดสอบโดยใช้ POST แต่สามารถใช้งานได้

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