Php, ошибка чтения файла, загруженные файлы не открываются

Я использую следующий код для загрузки файлов, которые хранятся вне общей папки.

  $mime_type = mime_content_type("{$_GET['file']}");
define("IMG_LOC","/var/www/domain.com/upload/");
$filename = $_GET['file'];
header('Content-Description: File Transfer');
header('Content-Type: '.$mime_type);
header('Content-Disposition: attachment; filename='.basename(IMG_LOC.$filename));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filename));
readfile($filename);
exit;

Проблема в том, что файл, загруженный с помощью этого сценария, не может быть использован. Excel открывается пустым, PowerPoint сообщает «есть ошибка чтения», а Word говорит, что пропал конвертер. Принимая во внимание, что если я загружаю те же файлы с помощью ftp и открываю их вручную, файлы открываются правильно, показывая, что файлы не повреждены.

Для информации, это вызывается с другой страницы как: file.php?file='. $filename

Любая помощь будет приветствоваться. Спасибо за ваше время.

0

Решение

Вы, похоже, не указали путь к вашему файлу:

  header('Content-Length: ' . filesize(IMG_LOC . $filename));
readfile(IMG_LOC . $filename);

Вы также должны добавить проверку имени файла, чтобы избежать проблем с безопасностью.

Если у вас все еще есть проблема, вы также должны проверить точный вывод скрипта, возможно, перед вашим файлом есть php-предупреждения или сообщения.

2

Другие решения

Я делаю вывод что $filename не абсолютный путь к файлу, который вы ищете, и, следовательно, почему вы определяете IMG_LOC постоянная с путем. Отсюда ясно, что filesize($filename)а также readfile($filename) вряд ли даст вам то, что вы хотите.

Попробуйте объединить константу перед $filename переменная вроде так …

header('Content-Length: ' . filesize(IMG_LOC . $filename));
readfile(IMG_LOC . $filename);

Также учтите, что этот код подвержен атакам с внедрением заголовка, а также другим проблемам безопасности, таким как пользователь, предоставляющий вам имя файла на вашем сервере, который вы, возможно, не захотите видеть. Например, если я вызову ваш скрипт со строкой запроса ?file=yourscript.php Я смогу загрузить ваш реальный код PHP и, возможно, увидеть любую конфиденциальную информацию, которую вы, возможно, не захотите раскрыть, например пароль вашей базы данных или что-то еще хуже.

Также, mime_content_type это осуждается функция и должна быть заменена на Fileinfo расширение вместо.

1

У вашего скрипта есть различные проблемы, которые в целом будут мешать ему должным образом за работой. Я грубо прохожу строки и оставляю некоторые комментарии, затем пишу небольшое резюме и предлагаю другой пример кода с включенными комментариями:

$mime_type = mime_content_type("{$_GET['file']}");

Вам не нужно оборачивать $_GET суперглобальный в фигурных скобках, а затем в двойных кавычках. Это просто не нужно для этого параметра. Вы, кажется, отвлекаетесь в этой точке.

Во всяком случае, эта вещь MIME-типа не является необходимой, так как MIME-тип не интересен, если вы хотите предложить скачать. Ты взял application/octet-stream вместо этого вы можете позаботиться позже о более конкретном типе пантомимы:

$mime_type = "application/octet-stream";

Тогда в неправильном положении вы определяете IMG_LOC постоянная:

define("IMG_LOC", "/var/www/domain.com/upload/");

Это относится к самой верхней части скрипта, так как вы определяете конфигурацию этим.

В соответствии:

$filename = $_GET['file'];

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

Следующие две строки более или менее правильны:

header('Content-Description: File Transfer');
header('Content-Type: '.$mime_type);

Для следующего заголовка:

header('Content-Disposition: attachment; filename='.basename(IMG_LOC.$filename));

Я бы извлек базовое имя ранее и просто передал переменную здесь. То же самое для заголовка content-length позже:

header('Content-Length: ' . filesize($filename));

Затем у вас есть этот блок кэширующих заголовков, так как вы отправляете файл с диска, я не думаю, что это действительно необходимо, поэтому я бы удалил их:

header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');

Строка readfile выглядит нормально, однако вы можете выполнить некоторую проверку ошибок:

readfile($filename);

И последняя строка, которую я не понимаю, так как скрипт в конце концов, зачем выходить?

exit;

Мои предложения после этого небольшого обзора:

Соберите информацию, какие файлы должны быть обслужены и как они должны быть названы. Сбор такой информации позволит вам закрыть проблему обхода каталога, которую вы должны закрыть в первую очередь.

Во-вторых, размещение логической части над выводом (и конфигурации над логикой) должно позволить вам упорядочить сценарий более полезным способом, что позволит вам легче обрабатывать проблемы с типом mime, например, когда вы поддерживаете сценарий (или кэшируете). если это действительно проблема).

<?php
/**
* download a file
*
* parameter:
*
*  file - name of the relative to upload folder
*/

const IMG_LOC = "/var/www/domain.com/upload";

// validate filename input
if (!isset($_GET['file'])) {
return;
}
$filename = $_GET['file'];
$path     = realpath(IMG_LOC . '/' . $filename);
if (0 !== strpos($path, IMG_LOC)) {
return;
}
if (!is_readable($filename)) {
return;
}

// obtain data
$basename  = basename($filename);
$mime_type = "application/octet-stream"; # can be improved later
$size      = filesize($path);

// output
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime_type);
header('Content-Disposition: attachment; filename=' . $basename);
header('Content-Length: ' . $size);
readfile($filename);
1
По вопросам рекламы [email protected]