Cách buộc tải tệp xuống bằng PHP


124

Tôi muốn yêu cầu tải xuống tệp khi người dùng truy cập trang web bằng PHP. Tôi nghĩ nó có liên quan gì đó file_get_contents, nhưng không chắc chắn về cách thực thi nó.

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

Sau khi tải xuống một tệp với header(location)nó sẽ không chuyển hướng đến trang khác. Nó chỉ dừng lại.


có thể trùng lặp của buộc trang download trong php
hakre

Câu trả lời:


233

Đọc tài liệu về hàm readfile tích hợp trong PHP

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

Ngoài ra, hãy đảm bảo thêm loại nội dung phù hợp dựa trên tệp ứng dụng / zip, ứng dụng / pdf, v.v. - nhưng chỉ khi bạn không muốn kích hoạt hộp thoại lưu dưới dạng.


6
Tại sao điều đó là bắt buộc?
john

Ý tôi là nếu nó không phải là tệp .exe chỉ có .Else này sẽ hoạt động tốt.
Pit Digger

7
Đừng quên tuôn ra ;-) ob_clean (); tuôn ra(); / * trước * / readfile ($ file_url);
Ash

Vì vậy, nếu bạn có một tệp lớn 10GB, php có cố gắng tải toàn bộ tệp đó không?
GDY

exit () nên được gọi là ở cuối để tránh bất kỳ vấn đề tiềm ẩn (nói từ kinh nghiệm :-)
ykay nói Khôi phục 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(); 
?>

Hoặc, khi tệp không thể mở được bằng trình duyệt, bạn chỉ có thể sử dụng Locationtiêu đề:

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

1
tên tệp trong tiêu đề của bạn không nên là $file(có chứa phần http) mà là một tên tệp hợp lệ.
Fabio,

1
Không có ứng dụng / loại phương tiện buộc tải xuống ; sử dụng ứng dụng / octet-stream để thay thế.
Gumbo

hoạt động rất tốt! Nhưng thêm 'vào tên tệp được lưu trữ. Vui lòng sử dụng: filename = ". Basename ($ file));
harry4516

@ harry4516 được sửa đổi theo phát hiện của bạn
Marek Sebera

Không hoạt động thân mến trong trường hợp hình ảnh png đối với tôi. Cảm ơn.
Kamlesh

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

đúng

hoặc tốt hơn một cho loại tệp exe

header("Location: $url");

@Fabio: Sẽ thêm nhiều detials hơn
genesis

Điều đó thật dễ dàng. Nó đã làm việc. File-Get_contents dùng để làm gì? Chỉ tò mò. Cảm ơn.
john

để tải nó xuống máy chủ của bạn
genesis

10
Chỉ cần xóa 'echo' trước 'readfile ()' vì giá trị trả về được chỉ định là "Trả về số byte được đọc từ tệp. Nếu xảy ra lỗi, FALSE được trả về và trừ khi hàm được gọi là @readfile (), một thông báo lỗi được in. ". Vì vậy, bạn sẽ kết thúc với nội dung của tệp + số nguyên ở cuối nội dung.
Mladen B.

22

Hiển thị tệp của bạn trước tiên và đặt giá trị của nó thành 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
Bạn đã có cái này từ đâu? Nó có vẻ mạnh mẽ
Axel A. García

@ jundell-agbo thú vị và cho một tệp crt?
fralbo

Điều này cũng hoạt động cho một tệp rất lớn. Giải pháp tuyệt vời. Nhưng `$ chunksize = 1 * (1024 * 1024);` chậm. Tôi đã thử với nhiều giá trị và nhận thấy rằng `$ chunksize = 8 * (1024 * 1024);` đang sử dụng tất cả băng thông có sẵn.
Linga

16

Trong trường hợp bạn phải tải xuống tệp có kích thước lớn hơn giới hạn bộ nhớ cho phép ( memory_limitcài đặt ini) dẫn đến PHP Fatal error: Allowed memory size of 5242880 bytes exhaustedlỗi, bạn có thể làm như sau:

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

Tôi vừa thử điều này và nó đã giết máy chủ của tôi. Đã viết rất nhiều lỗi vào nhật ký của tôi.
Daniel Williams

Sẽ rất hữu ích nếu bạn biết nội dung của các bản ghi.
Parziphal

Vâng, xin lỗi, tệp nhật ký quá lớn, tôi phải xóa nó, vì vậy tôi không thể xem được.
Daniel Williams

Tôi đã phải thiết lập một cái gì đó như thế này trên một máy chủ mà tôi có quyền truy cập rất hạn chế. Tập lệnh tải xuống của tôi liên tục chuyển hướng đến trang chủ và tôi không thể hiểu tại sao. Bây giờ tôi biết sự cố là do lỗi bộ nhớ và mã này đã giải quyết được nó.
Gavin

Nếu bạn chỉ sử dụng, ob_flush()có thể có lỗi: ob_flush (): không thể xóa bộ đệm. Không có bộ đệm để xả . quấn quanh nó với if (ob_get_level() > 0) {ob_flush();}(Tham khảo stackoverflow.com/a/9182133/128761 )
vee

11

Một sửa đổi của câu trả lời được chấp nhận ở trên, cũng phát hiện kiểu MIME trong thời gian chạy:

$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

Đoạn mã sau đây là một cách chính xác để triển khai dịch vụ tải xuống trong php như được giải thích trong hướng dẫn sau

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
Bạn có hai biến trong mã này: file_namefilePath. Thực hành viết mã không tốt lắm. Đặc biệt là không cần giải thích. Liên kết đến hướng dẫn bên ngoài không hữu ích.
Khom Nazid

2

thử cái này:

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


1

Bạn cũng có thể tải xuống trực tuyến, điều này sẽ tiêu tốn ít tài nguyên hơn đáng kể. thí dụ:

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

Trong ví dụ trên, tôi đang tải xuống test.zip (thực chất là zip studio android trên máy cục bộ của tôi). php: // output là một luồng chỉ ghi (thường được sử dụng bởi echo hoặc print). sau đó, bạn chỉ cần đặt các tiêu đề bắt buộc và gọi stream_copy_to_stream (nguồn, đích). Phương thức stream_copy_to_stream () hoạt động như một đường ống lấy đầu vào từ luồng nguồn (luồng đọc) và chuyển nó đến luồng đích (luồng ghi) và nó cũng tránh vấn đề hết bộ nhớ cho phép để bạn thực sự có thể tải xuống các tệp lớn hơn so với PHP của bạn memory_limit.


Ý bạn là gì khi tải xuống được phát trực tuyến?
Parsa Yazdani

1
@ParsaYazdani Có nghĩa là dữ liệu đã tải xuống sẽ được chia nhỏ. Vui lòng xem xét khái niệm phát trực tuyến để biết thêm chi tiết. Lưu ý: đây không phải là cách duy nhất để tải xuống tệp trong PHP. Nhưng nó chắc chắn là một trong những phương pháp dễ dàng nhất.
Saud Qureshi

0

Các câu trả lời ở trên tôi hoạt động. Tuy nhiên, tôi muốn đóng góp một phương pháp về cách thực hiện nó bằng cách sử dụng GET

trên trang html / php của bạn

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

download.phpchứa

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

điều này sẽ hoạt động trên mọi loại tệp. điều này không được kiểm tra bằng cách sử dụng POST, nhưng nó có thể hoạt động.

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.