Заголовки HTTP для загрузки файлов

Я написал сценарий PHP, который обрабатывает загрузку файлов, определяет, какой файл запрашивается, и устанавливает правильные заголовки HTTP, чтобы браузер действительно загрузил файл (а не отображал его в браузере).

Теперь у меня проблема, когда некоторые пользователи сообщают, что определенные файлы идентифицируются неправильно (поэтому, независимо от расширения, браузер будет считать это изображением в формате GIF). Я предполагаю, что это потому, что я не установил «Content-type» в заголовке ответа. Так ли это, скорее всего? Если да, то существует ли достаточно общий тип, который можно было бы использовать для всех файлов, вместо того, чтобы пытаться учитывать все возможные типы файлов?

В настоящее время я устанавливаю только значение «Content-disposition: attachment; filename = arandomf.ile»

Обновлять: Я следовал этому руководству здесь, чтобы создать более надежный процесс загрузки файлов (http://w-shadow.com/blog/2007/08/12/how-to-force-file-download-with-php/), но существует значительная задержка между выполнением сценария и появлением диалогового окна загрузки браузера. Может ли кто-нибудь определить узкое место, вызывающее это?

Вот моя реализация:

/**
 * Outputs the specified file to the browser.
 *
 * @param string $filePath the path to the file to output
 * @param string $fileName the name of the file
 * @param string $mimeType the type of file
 */
function outputFile($filePath, $fileName, $mimeType = '') {
    // Setup
    $mimeTypes = array(
        'pdf' => 'application/pdf',
        'txt' => 'text/plain',
        'html' => 'text/html',
        'exe' => 'application/octet-stream',
        'zip' => 'application/zip',
        'doc' => 'application/msword',
        'xls' => 'application/vnd.ms-excel',
        'ppt' => 'application/vnd.ms-powerpoint',
        'gif' => 'image/gif',
        'png' => 'image/png',
        'jpeg' => 'image/jpg',
        'jpg' => 'image/jpg',
        'php' => 'text/plain'
    );

    $fileSize = filesize($filePath);
    $fileName = rawurldecode($fileName);
    $fileExt = '';

    // Determine MIME Type
    if ($mimeType == '') {
        $fileExt = strtolower(substr(strrchr($filePath, '.'), 1));

        if (array_key_exists($fileExt, $mimeTypes)) {
            $mimeType = $mimeTypes[$fileExt];
        }
        else {
            $mimeType = 'application/force-download';
        }
    }

    // Disable Output Buffering
    @ob_end_clean();

    // IE Required
    if (ini_get('zlib.output_compression')) {
        ini_set('zlib.output_compression', 'Off');
    }

    // Send Headers
    header('Content-Type: ' . $mimeType);
    header('Content-Disposition: attachment; filename = "' . $fileName . '"');
    header('Content-Transfer-Encoding: binary');
    header('Accept-Ranges: bytes');

    // Send Headers: Prevent Caching of File
    header('Cache-Control: private');
    header('Pragma: private');
    header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');

    // Multipart-Download and Download Resuming Support
    if (isset($_SERVER['HTTP_RANGE'])) {
        list($a, $range) = explode('=', $_SERVER['HTTP_RANGE'], 2);
        list($range) = explode(',', $range, 2);
        list($range, $rangeEnd) = explode('-', $range);

        $range = intval($range);

        if (!$rangeEnd) {
            $rangeEnd = $fileSize - 1;
        }
        else {
            $rangeEnd = intval($rangeEnd);
        }

        $newLength = $rangeEnd - $range + 1;

        // Send Headers
        header('HTTP/1.1 206 Partial Content');
        header('Content-Length: ' . $newLength);
        header('Content-Range: bytes ' . $range - $rangeEnd / $size);
    }
    else {
        $newLength = $size;
        header('Content-Length: ' . $size);
    }

    // Output File
    $chunkSize = 1 * (1024*1024);
    $bytesSend = 0;

    if ($file = fopen($filePath, 'r')) {
        if (isset($_SERVER['HTTP_RANGE'])) {
            fseek($file, $range);

            while(!feof($file) && !connection_aborted() && $bytesSend < $newLength) {
                $buffer = fread($file, $chunkSize);
                echo $buffer;
                flush();
                $bytesSend += strlen($buffer);
            }

            fclose($file);
        }
    }
}

Аналогичная проблема здесь stackoverflow.com/questions/33946612/…

WebICT By Leo 26.11.2015 23:49
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
55
1
173 604
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Согласно RFC 2046 (многоцелевые расширения почты Интернета):

The recommended action for an implementation that receives an
"application/octet-stream" entity is to simply offer to put the data in a file

Так что я бы выбрал это.

Я согласен - application / octet-stream сообщает браузеру, что это общий двоичный файл, который приведет к его сохранению на диск

Marc Novakowski 22.12.2008 20:51

Но Content-disposition вернее. stackoverflow.com/questions/20508788/…

Paul Draper 25.04.2014 21:21

Вы можете попробовать это скрипт принудительной загрузки. Даже если вы не используете его, он, вероятно, укажет вам правильное направление:

<?php

$filename = $_GET['file'];

// required for IE, otherwise Content-disposition is ignored
if (ini_get('zlib.output_compression'))
  ini_set('zlib.output_compression', 'Off');

// addition by Jorg Weske
$file_extension = strtolower(substr(strrchr($filename,"."),1));

if ( $filename == "" ) 
{
  echo "<html><title>eLouai's Download Script</title><body>ERROR: download file NOT SPECIFIED. USE force-download.php?file=filepath</body></html>";
  exit;
} elseif ( ! file_exists( $filename ) ) 
{
  echo "<html><title>eLouai's Download Script</title><body>ERROR: File not found. USE force-download.php?file=filepath</body></html>";
  exit;
};
switch( $file_extension )
{
  case "pdf": $ctype = "application/pdf"; break;
  case "exe": $ctype = "application/octet-stream"; break;
  case "zip": $ctype = "application/zip"; break;
  case "doc": $ctype = "application/msword"; break;
  case "xls": $ctype = "application/vnd.ms-excel"; break;
  case "ppt": $ctype = "application/vnd.ms-powerpoint"; break;
  case "gif": $ctype = "image/gif"; break;
  case "png": $ctype = "image/png"; break;
  case "jpeg":
  case "jpg": $ctype = "image/jpg"; break;
  default: $ctype = "application/octet-stream";
}
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers 
header("Content-Type: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();

Обратите внимание, что Content-Type: application/force-download и Content-Transfer-Encoding: binary не являются стандартами HTTP. Они могут работать, но здесь бесполезны. RFC2616: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17

Flo Schild 29.05.2015 12:19

"приложение / принудительная загрузка" - не лучший вариант по умолчанию; "application / octet-stream" было бы лучше

Brian 04.03.2020 08:08

Как объясняется ссылкой Алекса, вам, вероятно, не хватает заголовка Content-Disposition поверх Content-Type.

Так что-то вроде этого:

Content-Disposition: attachment; filename = "MyFileName.ext"

Думаю должен быть attachment; filename = "MyFileName.ext". См. w3.org/Protocols/rfc2616/rfc2616-sec19.html.

B Seven 04.03.2016 01:38

Другие вопросы по теме