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

125

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

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

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

Джон
источник
возможный дубликат принуждения к загрузке файла с помощью PHP
пользователь

Ответы:

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
источник
Я просто попробовал это, и мой сервер был убит. Записал в журналы огромное количество ошибок.
Дэниел Уильямс,
Было бы полезно узнать, о чем были журналы.
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();
nqobile
источник
2

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

Это все, что вам нужно. "Monkey.gif" измените имя вашего файла. Если вам нужно загрузить с другого сервера, "monkey.gif" измените на " http://www.exsample.com/go.exe "

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

Мо Чан
источник