Nginx PHP с большими загрузками файлов (более 6 ГБ)

У меня очень странная проблема с загрузкой файлов размером более 6 ГБ. Мой процесс работает следующим образом:

  1. Файлы загружаются через Ajax в php-скрипт.
  2. Сценарий загрузки PHP принимает $ _FILE и копирует его в кусках, как в этом ответе на местоположение tmp.
  3. Расположение файла хранится в db
  4. Сценарий cron выведет файл на s3 позднее, снова используя функции fopen и буферизацию, чтобы поддерживать низкий уровень использования памяти

Моя конфигурация PHP (HHVM) и NGINX имеет свою конфигурацию, позволяющую до 16 ГБ файла, мой тестовый файл – всего 8 ГБ.

Вот странная часть, ajax ВСЕГДА будет время. Но файл успешно загружен, его копируют в местоположение tmp, место, хранящееся в db, s3 и т. Д. Но AJAX работает в течение часа даже ПОСЛЕ завершения выполнения (что занимает 10-15 минут) и только заканчивается при выходе из строя.

Что может заставить сервер не отправлять ответ только для больших файлов?

Также журналы ошибок на стороне сервера пустые.

Большая загрузка файла является дорогостоящей и подверженной ошибкам операции. Nginx и backend должны иметь правильные границы тайм-аута для обработки медленного ввода-вывода IO, если таковые имеются. Теоретически просто управлять загрузкой файлов с использованием кодирования multipart / form-data RFC 1867.

Согласно developer.mozilla.org в купе multipart / form-data, общий заголовок HTTP Content-Disposition – это заголовок, который можно использовать в подразделе многочастного тела, чтобы предоставить информацию о поле, к которому оно относится. Подчасти разделяется границей, определенной в заголовке Content-Type. Используемый на самом теле, Content-Disposition не влияет.

Посмотрим, что происходит во время загрузки файла:

1) клиент отправляет HTTP-запрос с содержимым файла на веб-сервер

2) веб-сервер принимает запрос и инициирует передачу данных (или возвращает ошибку 413, если размер файла превышает лимит)

3) webserver начинает заполнять буферы (зависит от размера файлов и буферов)

4) веб-сервер отправляет содержимое файла через файловый / сетевой сокет на бэкэнд

5) бэкэнд аутентифицирует первоначальный запрос

6) бэкэнд читает файл и разрезает заголовки (Content-Disposition, Content-Type)

7) Бэкэнд-дамп привел файл на диск

8) любые последующие процедуры, такие как изменения базы данных

client_body_in_file_only off;

Во время загрузки больших файлов возникает несколько проблем:

  • запрос тела HTTP выгружается на диск и переходит на бэкэнд, который обрабатывает и копирует файл
  • невозможно проверить подлинность запроса до того, как содержимое HTTP-запроса будет загружено на сервер
  • в то время как загрузка больших файлов backend редко требует самого содержимого файла

Начнем с того, что Nginx настроен на новое местоположение http: // backend / upload для получения большой загрузки файла, минимизируется взаимодействие с исходным кодом (Content-Legth: 0), файл хранится только на диске. Использование буферов Nginx выгружает файл на диск (файл, хранящийся во временном каталоге со случайным именем, его нельзя изменить), а затем запрос POST для backend к адресу http: // backend / file с именем файла в X-File- Заголовок заголовка.

Чтобы сохранить дополнительную информацию, вы можете использовать заголовки с первоначальным запросом POST. Например, если заголовки заголовков X-Original-File-Name из первоначальных запросов помогают вам сопоставлять файл и хранить необходимую информацию о карте в базе данных.

client_body_in_file_only on;

Посмотрим, как это произойдет:

1) настройте Nginx для выгрузки содержимого тела HTTP в файл и храните его в файле client_body_in_file_only;

2) создать новую конечную точку http: // backend / file для обработки сопоставления между именем файла temp и заголовком X-File-Name

4) запрос AJAX инструмента с заголовком X-File-Name Nginx будет использовать для отправки запроса на отправку сообщений с

Конфигурация:

location /upload { client_body_temp_path /tmp/; client_body_in_file_only on; client_body_buffer_size 1M; client_max_body_size 7G; proxy_pass_request_headers on; proxy_set_header X-File-Name $request_body_file; proxy_set_body off; proxy_redirect off; proxy_pass http://backend/file; } 

Опция конфигурации Nginx client_body_in_file_only несовместима с многофайловой загрузкой данных, но вы можете использовать ее с AJAX, т.е. XMLHttpRequest2 (двоичные данные).

Если вам нужна внутренняя аутентификация, можно использовать только auth_request , например:

 location = /upload { auth_request /upload/authenticate; ... } location = /upload/authenticate { internal; proxy_set_body off; proxy_pass http://backend; } 

client_body_in_file_only on; auth_request on;

Предварительная загрузка логики аутентификации защищает от неавторизованных запросов независимо от начального размера длины заказа POST.