Как принудительно загрузить файл с помощью PHP


125

Я хочу, чтобы файл загружался при посещении пользователем веб-страницы с PHP. Я думаю, это как-то связано file_get_contents, но не знаю, как это сделать.

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

После загрузки файла с header(location)него не происходит перенаправление на другую страницу. Это просто останавливается.



Ответы:


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

Также убедитесь, что добавлен правильный тип содержимого на основе вашего файла application / zip, application / pdf и т. Д. - но только если вы не хотите запускать диалог сохранения как.


6
Зачем это нужно?
Джон

Я имею в виду, если это не файл .exe, а не только. Иначе это должно работать нормально.
Pit Digger

7
Не забывайте промывать ;-) ob_clean (); промывать(); / * перед * / readfile ($ file_url);
Эш

Итак, если у вас большой файл размером 10 ГБ, php пытается загрузить этот файл целиком?
GDY 03

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), а должно быть допустимым именем файла.
Фабио

1
Там нет приложения / принудительной загрузка типа носителя; вместо этого используйте application / octet-stream .
Gumbo

работает очень красиво! Но добавляет 'к сохраненному имени файла. Пожалуйста, используйте: filename = ". Basename ($ file));
harry4516

@ harry4516 изменен в соответствии с вашими
выводами

Не работает дорогая в случае изображения PNG для меня. Спасибо.
Камлеш

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? Просто любопытно. Спасибо.
Джон

чтобы загрузить его на свой сервер
Genesis

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
Откуда вы это взяли? Кажется мощным
Аксель А. Гарсия

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

Я просто попробовал это, и мой сервер был убит. Записал в журналы огромное количество ошибок.
Дэниел Уильямс,

Было бы полезно узнать, о чем были журналы.
Parziphal

Да, извините, файл журнала стал настолько большим, что мне пришлось просто уничтожить его, так что я не смог увидеть.
Дэниел Уильямс,

Мне пришлось настроить что-то подобное на сервере, к которому у меня был очень ограниченный доступ. Мой сценарий загрузки продолжал перенаправлять на домашнюю страницу, и я не мог понять почему. Теперь я знаю, что проблема была в ошибке памяти, и этот код решил ее.
Гэвин

Если вы просто используете, 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. Не очень хорошая практика кодирования. Тем более без объяснения причин. Ссылки на внешнее руководство бесполезны.
Хом Назид

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 (который на самом деле был zip-архивом студии Android на моем локальном компьютере). php: // output - это поток только для записи (обычно используется echo или print). после этого вам просто нужно установить требуемые заголовки и вызвать stream_copy_to_stream (источник, место назначения). Метод stream_copy_to_stream () действует как канал, который принимает входные данные из исходного потока (поток чтения) и направляет его в целевой поток (поток записи), а также позволяет избежать проблемы исчерпания разрешенной памяти, поэтому вы можете фактически загружать файлы большего размера чем ваш PHP memory_limit.


Что вы имеете в виду под потоковой загрузкой?
Парса Яздани

1
@ParsaYazdani Это означает, что загруженные данные будут разбиты на части. Пожалуйста, ознакомьтесь с концепцией потоковой передачи для получения более подробной информации. Примечание: это не единственный способ потоковой (чанк) загрузки файла в PHP. Но это, безусловно, один из самых простых способов.
Сауд Куреши

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, но может работать.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.