PHP로 파일 다운로드를 강제하는 방법


122

사용자가 PHP로 웹 페이지를 방문 할 때 파일을 다운로드하도록하고 싶습니다. 와 관련이 있다고 생각 file_get_contents하지만 실행 방법을 모르겠습니다.

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

파일을 다운로드 한 후 header(location)다른 페이지로 리디렉션되지 않습니다. 그냥 멈 춥니 다.



답변:


232

내장 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); 

또한 파일 application / zip, application / pdf 등을 기반으로 적절한 콘텐츠 유형을 추가해야합니다. 그러나 다른 이름으로 저장 대화 상자를 트리거하지 않으려는 경우에만 해당됩니다.


6
왜 필요한가요?
john

내 말은 .exe 파일이 아닌 .Else가 아니라면 잘 작동합니다.
Pit Digger

7
플러시하는 것을 잊지 마세요 ;-) ob_clean (); 플러시(); / * 이전 * / readfile ($ file_url);
Ash

10GB의 큰 파일이 있다면 PHP는 전체 파일을로드하려고합니까?
GDY

출구 () :-) 경험에서 말하기 (잠재적 인 문제를 방지하기 위해 마지막에 호출되어야한다
ykay는 분석 재개 모니카 말한다

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
응용 프로그램 / 강제 다운로드 미디어 유형 이 없습니다 . 사용하는 응용 프로그램 / 옥텟 스트림을 대신.
Gumbo

아주 잘 작동합니다! 그러나 저장된 파일 이름에 '를 추가합니다. 사용하십시오 : filename = ". basename ($ file));
harry4516

@ harry4516 당신의 연구 결과에 따라 수정
마렉 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 : 더 많은 detials를 추가합니다
제네시스

매우 쉬웠습니다. 효과가있었습니다. 그렇다면 file-Get_contents는 무엇입니까? 그냥 궁금해. 감사.
john

서버에 다운로드하기
창세기

10
반환 값은 "파일에서 읽은 바이트 수를 반환합니다. 오류가 발생하면 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
이걸 어디서 얻었나요? 그것은 강력한 것
악셀 A. 가르시아에게

@ jundell-agbo 흥미롭고 crt 파일에 대해?
fralbo

이것은 매우 큰 파일에서도 작동합니다. 훌륭한 솔루션입니다. 하지만`$ chunksize = 1 * (1024 * 1024);`는 느립니다. 여러 값으로 시도한 결과`$ chunksize = 8 * (1024 * 1024);`가 사용 가능한 모든 대역폭을 사용하고 있음을 알았습니다.
Linga

16

허용 된 메모리 제한 ( memory_limitini 설정) 보다 큰 파일을 다운로드해야하는 경우 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 )
V 시리즈

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_namefilePath. 그다지 좋은 코딩 연습이 아닙니다. 특히 설명없이. 외부 튜토리얼에 연결하는 것은 도움이되지 않습니다.
Khom Nazid

2

이 시도:

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


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 스튜디오 zip)을 다운로드하고 있습니다. php : // output은 쓰기 전용 스트림입니다 (일반적으로 echo 또는 print에서 사용됨). 그 후에 필요한 헤더를 설정하고 stream_copy_to_stream (source, destination)을 호출하면됩니다. stream_copy_to_stream () 메서드는 소스 스트림 (읽기 스트림)에서 입력을 가져 와서 대상 스트림 (쓰기 스트림)으로 파이프하는 파이프 역할을하며 허용 된 메모리 소모 문제를 방지하여 실제로 더 큰 파일을 다운로드 할 수 있습니다. 당신의 PHP보다 memory_limit.


다운로드가 스트리밍된다는 것은 무엇을 의미합니까?
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.