Мне нужно доставлять клиентам большие файлы, такие как file.zip (~ 2 ГБ), с уникальным URL для каждого клиента. Затем я перенаправлю (с .htaccess) ссылку на скачивание клиента example.com/download/f6zDaq/file.zip на что-то вроде

example.com/download.php?id=f6zDaq&file=file.zip

Но так как файлы большие, я не хочу, чтобы факт, что PHP обрабатывает загрузку (вместо того, чтобы просто Apache обрабатывал это), был проблемой производительности ЦП / ОЗУ для моего сервера. В конце концов, запрос PHP делает это с использованием нового слоя, поэтому он может вызвать такую проблему, если не будет выполнен должным образом.

Вопрос: какие из следующих решений являются наилучшими? (в частности, с точки зрения ЦП / ОЗУ)?

  • 1: решение PHP с application/download

    header('Content-Type: application/download');
    header('Content-Disposition: attachment; filename=file.zip');
    readfile("/path/to/file.zip");
    

    Загрузка ЦП, измеренная при загрузке: 13,6%.

  • 1bis: решение PHP с application/octet-stream (из примера № 1 этой страницы )

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=file.zip');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize('file.zip'));
    readfile("/path/to/file.zip");
    
  • 1ter: решение PHP с application/octet-stream (из здесь):

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename=file.zip'); 
    header('Content-Transfer-Encoding: binary'); // additional line
    header('Connection: Keep-Alive');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); // additional line
    header('Pragma: public');
    header('Content-Length: ' . filesize('file.zip'));
    readfile("/path/to/file.zip");
    
  • 1-й квартал: еще один вариант PHP с application/force-download (отредактировано; поступает из здесь):

    header("Content-Disposition: attachment; filename=file.zip");
    header("Content-Type: application/force-download");
    header("Content-Length: " . filesize($file));
    header("Connection: close");
    
  • 2: решение Apache, без участия PHP: пусть Apache обслуживает файл и использует .htaccess для предоставления разных URL для одного и того же файла (можно написать множество способов сделать это). С точки зрения производительности это похоже на то, чтобы клиент мог загрузить example.com/file.zip, обслуживаемый сервером Apache.

  • 3: другое решение PHP. Это, вероятно, будет работать:

    $myfile = file_get_contents("file.zip");
    echo $myfile;
    

    но разве это не попросит PHP загрузить весь контент в память? (что было бы плохо с точки зрения производительности!)

  • 4: Просто выполните перенаправление header("Location: /abcd/file.zip");, как описано в Файл с коротким URL-адресом, загруженный с оригинальным именем файла.

    Проблема с этим решением: это раскрывает фактическое местоположение файла

     example.com/abcd/file.zip
    

    конечному пользователю (который может затем использовать или поделиться этим URL без аутентификации), который не нужен ...

    Но с другой стороны, он намного легче для процессора, так как PHP просто перенаправляет запрос и не доставляет сам файл.

    Загрузка ЦП, измеренная при загрузке: 10,6%.


Примечание. файл чтения сообщает:

readfile () сам по себе не создает проблем с памятью, даже при отправке больших файлов. Если вы столкнулись с ошибкой нехватки памяти, убедитесь, что выходная буферизация отключена с помощью ob_get_level ().

но я хотел быть на 100% уверенным, что он не будет медленнее / потреблять больше ресурсов ЦП и ОЗУ, чем чистое решение Apache.

6
Basj 31 Авг 2017 в 22:45

5 ответов

Лучший ответ

Вы можете использовать X-Accel-Redirect когда ваш веб-сервер Nginx. Для Apache это mod_xsendfile с заголовком X-Sendfile.

<?php
header('X-Accel-Redirect: /download/f6zDaq/file.zip');

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

1
Dai Jie 29 Апр 2019 в 19:44

Я бы пошел с readfile. Я использовал его годами, и у меня никогда не было проблем с памятью, даже при работе на 128 МБ VPS.

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

3
ThoriumBR 31 Авг 2017 в 19:53

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

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

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

Таким образом, из ваших представленных вариантов, я рекомендую

1ter

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

Приложение: здесь сказано, что работа с PHP - не лучшая идея, если вам не нужны функции управления заголовками и контроль .htaccess, потому что это просто добавляет больше вычислительной мощности. Безусловно, лучшим способом было бы просто иметь файлы в доступном каталоге. .htaccess может переписать доступ к файлам и папкам, а не только к PHP-скриптам.

Чтобы создать защищенные папки загрузки на основе Apache:

Options +FollowSymLinks
RewriteEngine On
RewriteRule ^/user/files/folder1.*$ http://example.com/userfiles/ [R=301,L]

Затем, если вам нужно защитить его паролем, вместо использования PHP, используйте Apache (который уже установлен в большинстве установок PHP). Это можно сделать, включив файл .htaccess в целевую папку (если вы динамически создаете пользователей, вам может потребоваться создать сценарий для их создания для каждого нового пользователя) и убедитесь, что apache подготовлен для обработки паролей:

AuthType Basic
AuthName "Authentication Required"
AuthUserFile "/user/password/.htpasswd"
Require valid-user

(Подробнее см. Здесь: Настройка паролей Apache).

После этого убедитесь, что в каталоге паролей есть файл .htpasswd в формате username: password / hashedpassword.

Например.:

andreas:$apr1$dHjB0/..$mkTTbqwpK/0h/rz4ZeN8M0
john:$apr1$IHaD0/..$N9ne/Bqnh8.MyOtvKU56j1

Теперь, если вы не хотите, чтобы они передавали пароль каждый раз, в ссылке для скачивания включите доступ

<a href="user:pass@http://example.com/userfiles/myCoolZip.zip">Link (hopefully behind a password-protected interface.)</a> 

[Примечание: НЕ используйте метод прямой ссылки на пароль, если пароли не назначаются случайным образом для каждого файла.]

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

ВНИМАНИЕ :

Теперь, при этом, файлы будут доступны людям, которым предоставлена полная ссылка (с именем пользователя / паролем). Таким образом, они будут такими же безопасными (или такими же небезопасными), как протоколы https (или http, если вы позволите) вашего сервера, а также ваши пользователи, которые делятся или не делятся ссылками.

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

4
liljoshu 29 Апр 2019 в 21:59

Вы можете использовать .htaccess для перенаправления запроса в файл, сохраняя структуру постоянной ссылки:

RewriteEngine On
RewriteBase /
RewriteRule ^download\/([^\/]+)\/file.zip download.php?id=$1 [L,NC]

Затем в своем download.php вы можете проверить, является ли предоставленный идентификатор действительным:

// Path to file
$file = 'file.zip';

// If the ID is valid
if ($condition) {
    header("Content-Disposition: attachment; filename=\"" . basename($file) . "\"");
    header("Content-Type: application/force-download");
    header("Content-Length: " . filesize($file));
    header("Connection: close");
} else {
    // Handle invalid ids
    header('Location: /');
}

Когда пользователь посещает действительный URL http://example.com/download/f6zDaq/file.zip, загрузка начнется и соединение будет закрыто.

Если пользователь посещает недействительный URL, он будет перенаправлен на домашнюю страницу.

7
Chin Leung 29 Апр 2019 в 19:30