PHP ile dosya indirmeye nasıl zorlanır


125

Kullanıcı PHP ile bir web sayfasını ziyaret ettiğinde bir dosyanın indirilmesini zorunlu kılmak istiyorum. Sanırım bununla bir ilgisi var file_get_contents, ancak nasıl uygulanacağından emin değilim.

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

Bir dosyayı indirdikten sonra header(location)başka bir sayfaya yönlendirmiyor. Sadece durur.



Yanıtlar:


234

Yerleşik PHP işlevi okuma dosyası hakkındaki belgeleri okuyun

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

Ayrıca dosya uygulamanıza / zip, uygulama / pdf vb. Dayalı olarak uygun içerik türü eklediğinizden emin olun - ancak yalnızca farklı kaydetme iletişim kutusunu tetiklemek istemiyorsanız.


6
Bu neden gerekli?
John

Demek istediğim, .exe dosyası dışında sadece .Else bu iyi çalışmalı.
Pit Digger

7
Boşaltmayı unutmayın ;-) ob_clean (); ) (fışkırma; / * önce * / readfile ($ file_url);
Kül

Öyleyse 10GB büyük bir dosyanız varsa, php tüm dosyayı yüklemeye çalışır?
GDY

Olası sorunlardan kaçınmak için sonda exit () çağrılmalıdır (deneyimden
yola çıkarak

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

Veya dosya tarayıcı ile açılamadığında, sadece Locationbaşlığı kullanabilirsiniz :

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

1
Başlığınızdaki dosya adı $file(http bölümünü içeren) olmamalı , geçerli bir dosya adı olmalıdır.
Fabio

1
Diye bir şey yok uygulama / zorla indirme medya türü; kullanmak uygulama / sayı grubunu-akışı yerine.
Gumbo

çok güzel çalışıyor! Ancak depolanan dosya adına 'ekler. Lütfen şunu kullanın: dosyaadı = ".
Basename

@ harry4516 bulgunuza göre değiştirildi
Marek Sebera

Benim için png görüntüsü durumunda canım çalışmıyor. Teşekkürler.
Kamlesh

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

doğru

veya exe tipi dosyalar için daha iyisi

header("Location: $url");

@Fabio: Daha fazla detial ekleyecek
genesis

Bu çok kolaydı. İşe yaradı. O halde-Get_contents dosyası nedir? Sadece merak. Teşekkürler.
john

sunucunuza indirmek için
genesis

10
Dönüş değeri "dosyadan okunan bayt sayısını döndürür. Bir hata oluşursa, FALSE döndürülür ve işlev @readfile () olarak çağrılmadıkça," readfile () "dan önce" echo "yu kaldırın, bir hata mesajı yazdırılır. " Böylece, içeriğin sonunda dosyanın içeriği + tam sayı numarasıyla karşılaşacaksınız.
Mladen B.

22

Önce dosyanızı görüntüleyin ve değerini url olarak ayarlayın.

index.php

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

indir.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
Bunu nereden aldın? Güçlü görünüyor
Axel A. Garcia

@ jundell-agbo ilginç ve crt dosyası için?
fralbo

Bu da çok büyük bir dosya için çalışıyor. Harika çözüm. Ama` $ chunksize = 1 * (1024 * 1024); `yavaş. Birden çok değerle denedim ve "$ chunksize = 8 * (1024 * 1024);" 'ün mevcut tüm bant genişliğini kullandığını fark ettim.
Linga

16

İzin verilen bellek sınırından ( memory_limitini ayarı) daha büyük boyutta bir dosya indirmeniz gerekirse , bu PHP Fatal error: Allowed memory size of 5242880 bytes exhaustedhataya neden olur , bunu yapabilirsiniz:

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

Bunu denedim ve sunucumu öldürdü. Günlüklerime çok sayıda hata yazdı.
Daniel Williams

Günlüklerin ne hakkında olduğunu bilmek faydalı olacaktır.
Parziphal

Evet üzgünüm, günlük dosyası o kadar büyüdü ki, onu bombalamak zorunda kaldım, bu yüzden göremedim.
Daniel Williams

Çok sınırlı erişime sahip olduğum bir sunucuda böyle bir şey kurmak zorunda kalıyordum. İndirme komut dosyam ana sayfaya yönlendirmeye devam etti ve nedenini anlayamadım. Şimdi sorunun bir hafıza hatası olduğunu ve bu kodun çözdüğünü biliyorum.
Gavin

Sadece kullanırsanız ob_flush(), şu hata olabilir: ob_flush (): arabelleği temizleyemedi. Yıkanacak tampon yok . etrafına sarın if (ob_get_level() > 0) {ob_flush();}(Reference stackoverflow.com/a/9182133/128761 )
vee

11

Yukarıda kabul edilen yanıtın, çalışma zamanında MIME türünü de algılayan bir değişikliği:

$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

Aşağıdaki kod, aşağıdaki öğreticide açıklandığı gibi php'de bir indirme hizmeti uygulamanın doğru bir yoludur.

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
Bu kodda iki değişkeniniz var: file_nameve filePath. Çok iyi kodlama uygulaması değil. Özellikle açıklama olmadan. Harici eğiticiye bağlantı vermek yardımcı olmuyor.
Khom Nazid

2

bunu dene:

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


1

İndirmeyi akış olarak da yapabilirsiniz, bu da önemli ölçüde daha az kaynak tüketir. misal:

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

Yukarıdaki örnekte, bir test.zip indiriyorum (aslında yerel makinemdeki android stüdyo zip dosyasıydı). php: // output salt yazılır bir akımdır (genellikle echo veya print tarafından kullanılır). bundan sonra, gerekli başlıkları ayarlamanız ve stream_copy_to_stream (kaynak, hedef) çağrısı yapmanız yeterlidir. stream_copy_to_stream () yöntemi, girdiyi kaynak akıştan alan (okuma akışı) ve onu hedef akışa yönlendiren (yazma akışı) bir kanal görevi görür ve ayrıca, izin verilen belleğin tükenmesi sorununu ortadan kaldırır, böylece daha büyük dosyaları gerçekten indirebilirsiniz PHP'nizden memory_limit.


İndirme akışı ile ne demek istiyorsun?
Parsa Yazdani

1
@ParsaYazdani İndirilen verilerin parçalanacağı anlamına gelir. Daha fazla ayrıntı için lütfen akış kavramına bakın. Not: Bu, dosyayı PHP'de akış (yığın) olarak indiren tek şey değildir. Ama kesinlikle en kolay yöntemlerden biridir.
Saud Qureshi

0

Yukarıdaki cevaplar işe yarıyor. Ancak, GET kullanarak nasıl gerçekleştirileceğine dair bir yöntemle katkıda bulunmak istiyorum

html / php sayfanızda

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

ve download.phpiçerir

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

bu herhangi bir dosya türü üzerinde çalışmalıdır. bu POST kullanılarak test edilmemiştir, ancak işe yarayabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.