Nextcloud. Включить загрузку 100-гигабайтных файлов

Рейтинг:   / 1
ПлохоОтлично 

Коллега на работе пожаловался, что не может загрузить большой 100-гиговый файл на корпоративный Nextcloud. А я напомню, что рабочий сервер я обновлял и настраивал по этой статье: 

https://typical-admin.ru/item/99-nextcloud-update-18-to-30

Чтобы можно было загружать такие большие файлы на Nextcloud, требуется изменить несколько конфигов. Начну с необязательного. В /var/www/html/nextcloud/config/config.php рекомендуется добавить вот такую строчку: 

'max_chunk_size' => 104857600,

По умолчанию клиенты Nextcloud (и сам сервер) обычно и так используют чанки ~100 MiB, поэтому ручная установка в 100 MiB часто просто дублирует поведение по умолчанию. Но плюс явной установки в том, что гарантируется одинаковое поведение для всех клиентов (браузер, desktop, WebDAV) и предотвращаются ситуации, когда клиент решит отправлять слишком большие чанки (и тогда может сработать upload_max_filesize или post_max_size).

Далее будут уже обязательные изменения конфигов. В /etc/php/8.3/fpm/php.ini нужно поправить вот эти параметры: 

max_execution_time = 43200
max_input_time = 43200
upload_max_filesize = 1024M
post_max_size = 1024M

Nextcloud НЕ грузит 100 ГБ одним файлом, он режет его на чанки (chunked upload). Поэтому нужно увеличить время на обработку загрузки. max_execution_time — параметр в PHP, который задаёт максимальное время в секундах, в течение которого скрипт должен полностью загрузиться. Если этого не происходит, работа скрипта завершается. max_input_time — директива в PHP, которая задаёт максимальное время в секундах, в течение которого скрипт должен разобрать все входные данные, переданные запросами вроде POST или GET. upload_max_filesize — директива в PHP, которая определяет максимальный размер загружаемых файлов на сервер. post_max_size — директива в PHP, которая устанавливает максимальный размер данных, отправленных методом POST, включая размер загружаемых файлов.

Далее пойдут настройки Apache под большие загрузки. Создаю для этого отдельный конфиг:

nano /etc/apache2/conf-available/nextcloud-large-upload.conf

Его содержимое будет таким:

Timeout 43200
ProxyTimeout 43200
LimitRequestBody 214748364800
RequestReadTimeout header=20-40,MinRate=500 body=3600,MinRate=500

Timeout — общий сетевой таймаут, который определяет, как долго Apache готов ждать завершения блокирующих операций сети там, где нет отдельных переопределений. ProxyTimeout — директива, которая настраивает время ожидания для прокси-запросов. Позволяет установить время, в течение которого сервер будет ждать ответа от бэкенд-сервера, к которому он проксирует запросы. LimitRequestBody позволяет задавать максимально допустимый размер тела клиентского запроса. В Apache 2.4.54+ по умолчанию стоит 1 GiB, что прерывает запросы файлов >1 GB. Нужно увеличить этот лимит, 214748364800 = 200 GiB. RequestReadTimeout — директива в Apache HTTP Server, которая устанавливает лимиты времени на чтение заголовков (headers) и тела (body) запроса. header=20-40,MinRate=500 — даёт 20 сек на начальное чтение заголовков, расширяет до 40 сек с учётом минимальной скорости передачи 500 B/s; это защищает от очень медленных/подозрительных клиентов. body=3600,MinRate=500 — на чтение тела даётся до 3600 с с минимальной скоростью передачи (MinRate) 500 bytes/sec; медленные соединения будут прерваны, но нормальные — пройдут.

Теперь нужно включить этот конфиг:

a2enconf nextcloud-large-upload

и перезапустить apache и php-fpm:

service php8.3-fpm restart
service apache2 restart

В целом, тут можно было бы и закончить. Но, в моём случае, загрузка таких больших файлов требуется очень редко, поэтому руководитель сказал, что будем включать эту возможность вручную по мере необходимости, а в остальное время настройки пускай остаются прежними. И, чтобы упростить процесс включения/отключения, я попросил у chatgpt скрипт для этого. Прикладываю этот скрипт сюда же:

https://disk.yandex.ru/d/duGEwspnTtRc4A

#!/usr/bin/env bash
# /usr/local/bin/gigafiles
#
# Скрипт переключает настройки PHP/Apache для поддержки редких загрузок очень больших файлов (≈100 ГБ).
# Usage:
#   sudo gigafiles on   - включить режим "под большие файлы"
#   sudo gigafiles off  - вернуть прежние значения
#
# Что делает:
# 1) Создаёт бэкап /etc/php/8.3/fpm/php.ini с меткой времени.
# 2) Заменяет или добавляет в php.ini четыре директивы:
#      max_execution_time
#      max_input_time
#      upload_max_filesize
#      post_max_size
#    Значения зависят от аргумента on/off.
# 3) Перезагружает php-fpm (reload, при проблеме — restart).
# 4) Включает/выключает конфигурацию Apache /etc/apache2/conf-available/nextcloud-large-upload.conf
#    через a2enconf / a2disconf.
# 5) Проверяет конфигурацию Apache (apachectl configtest) и, если проверка прошла,
#    выполняет systemctl reload apache2. Если проверка упала — возвращает php.ini из бэкапа.
#
# Права: требуется root (run via sudo).
#
set -euo pipefail

# Путь к php.ini
PHPINI="/etc/php/8.3/fpm/php.ini"

# Имя конфигурации Apache (без .conf). Используется a2enconf/a2disconf.
APACHE_CONF_NAME="nextcloud-large-upload"
APACHE_CONF="/etc/apache2/conf-available/${APACHE_CONF_NAME}.conf"

# Имена systemd-сервисов, которые будем перезагружать
PHP_FPM_SERVICE="php8.3-fpm"
APACHE_SERVICE="apache2"

# Утилиты
APACHECTL="$(command -v apachectl || true)"

# Вспомогательные функции

# timestamp - генерация метки времени для бэкапов
timestamp() { date +"%Y%m%d-%H%M%S"; }

# die - печатает сообщение об ошибке и завершает работу
die() { echo "ERROR: $*" >&2; exit 1; }

# Проверка прав root
if [ "$(id -u)" -ne 0 ]; then
  die "Этот скрипт должен запускаться от root (используйте sudo)."
fi

# Проверяем входной параметр
if [ $# -ne 1 ]; then
  echo "Использование: $0 on|off"
  exit 2
fi

ACTION="$1"
if [[ "$ACTION" != "on" && "$ACTION" != "off" ]]; then
  die "Неверный аргумент: $ACTION. Используйте 'on' или 'off'."
fi

# Проверка наличия php.ini
if [ ! -f "$PHPINI" ]; then
  die "php.ini не найден по пути $PHPINI"
fi

# Создаём бэкап php.ini с меткой времени.
BACKUP="${PHPINI}.bak.gigafiles.$(timestamp)"
cp -a "$PHPINI" "$BACKUP"
echo "Создан бэкап php.ini -> $BACKUP"

# Функция: заменить директиву в php.ini, либо добавить её в конец файла, если не найдена.
# Аргументы: ключ, значение
replace_or_append() {
  local key="$1"
  local value="$2"

  # Если директива найдена (игнорируем регистр), заменяем всю строку.
  # Поддерживаем формат: key = value
  if grep -Ei "^\s*${key}\s*=" "$PHPINI" >/dev/null 2>&1; then
    # sed: заменить первую найденную строку формата "key = anything" на "key = value".
    sed -ri "s#^\s*(${key})\s*=.*#\1 = ${value}#I" "$PHPINI"
    echo "Заменено: ${key} -> ${value}"
  else
    # Если директивы нет, просто добавляем в конец файла.
    echo -e "\n; added by gigafiles\n${key} = ${value}" >> "$PHPINI"
    echo "Добавлено: ${key} -> ${value}"
  fi
}

# Устанавливаем новые значения в зависимости от режима on/off
if [ "$ACTION" = "on" ]; then
  # Режим "включить большие файлы"
  NEW_max_execution_time="43200"   # 12 часов
  NEW_max_input_time="43200"       # 12 часов
  NEW_upload_max_filesize="1024M"  # 1 GiB (предел чанка)
  NEW_post_max_size="1024M"        # 1 GiB
else
  # Режим "выключить — вернуть стандартные значения"
  NEW_max_execution_time="60"
  NEW_max_input_time="60"
  NEW_upload_max_filesize="512M"
  NEW_post_max_size="512M"
fi

# Вносим изменения в php.ini (с заменой или добавлением)
# Комментарии рядом с записями помогут понять, что добавил скрипт.
replace_or_append "max_execution_time" "$NEW_max_execution_time"
replace_or_append "max_input_time" "$NEW_max_input_time"
replace_or_append "upload_max_filesize" "$NEW_upload_max_filesize"
replace_or_append "post_max_size" "$NEW_post_max_size"

# Функция для отката php.ini в случае ошибки (восстановление из бэкапа)
rollback_phpini() {
  if [ -f "$BACKUP" ]; then
    cp -a "$BACKUP" "$PHPINI"
    echo "php.ini восстановлен из бэкапа $BACKUP"
  else
    echo "Бэкап $BACKUP не найден — невозможно восстановить php.ini" >&2
  fi
}

# Перезагрузка php-fpm:
# Попытка reload (без прерывания соединений), при неудаче — restart.
echo "Перезагрузка $PHP_FPM_SERVICE..."
if systemctl is-active --quiet "$PHP_FPM_SERVICE"; then
  if systemctl reload "$PHP_FPM_SERVICE"; then
    echo "$PHP_FPM_SERVICE успешно перезагружен (reload)."
  else
    echo "Reload не удался — пробую restart $PHP_FPM_SERVICE..."
    systemctl restart "$PHP_FPM_SERVICE"
    echo "$PHP_FPM_SERVICE успешно перезапущен (restart)."
  fi
else
  # Если сервис не активен, запустим его.
  systemctl start "$PHP_FPM_SERVICE"
  echo "$PHP_FPM_SERVICE запущен."
fi

# Включение/отключение Apache конфигурации
if [ "$ACTION" = "on" ]; then
  if [ ! -f "$APACHE_CONF" ]; then
    echo "Внимание: $APACHE_CONF не найден; пропускаю a2enconf (файл должен быть создан вручную)." >&2
  else
    echo "Включаем Apache конфигурацию: $APACHE_CONF_NAME"
    a2enconf "$APACHE_CONF_NAME" >/dev/null || true
  fi
else
  if [ -f "$APACHE_CONF" ]; then
    echo "Отключаем Apache конфигурацию: $APACHE_CONF_NAME"
    a2disconf "$APACHE_CONF_NAME" >/dev/null || true
  else
    echo "Apache конфигурация $APACHE_CONF не найдена; ничего не делаю."
  fi
fi

# Проверка конфигурации Apache и reload:
# Если apachectl отсутствует, предупредим, но попробуем reload (systemctl).
if [ -n "$APACHECTL" ]; then
  echo "Тест конфигурации Apache..."
  if apachectl configtest >/dev/null 2>&1; then
    echo "apachectl configtest: OK"
    echo "Reloading $APACHE_SERVICE..."
    systemctl reload "$APACHE_SERVICE"
    echo "$APACHE_SERVICE reloaded."
  else
    # Если проверка упала — откатываем php.ini и сообщаем пользователю.
    echo "Ошибка: apachectl configtest не прошёл. Восстанавливаю php.ini из бэкапа и прекращаю."
    rollback_phpini
    # Старайтесь восстановить php-fpm состояние: reload чтобы вернуть прежние значения из бэкапа
    systemctl reload "$PHP_FPM_SERVICE" || true
    # Показываем тест для диагностики
    echo "Вывод apachectl configtest для диагностики:"
    apachectl configtest || true
    die "apachectl configtest завершился с ошибкой — проверьте конфиги Apache."
  fi
else
  # Если apachectl не найден (маловероятно на Debian), используем systemctl reload и предупреждаем.
  echo "Предупреждение: apachectl не найден. Попытка systemctl reload apache2..."
  systemctl reload "$APACHE_SERVICE" || {
    echo "systemctl reload apache2 не удался. Восстанавливаю php.ini из бэкапа."
    rollback_phpini
    systemctl reload "$PHP_FPM_SERVICE" || true
    die "Не удалось перезагрузить Apache; проверьте конфигурацию вручную."
  }
  echo "Apache перезагружён через systemctl."
fi

echo "Готово. Текущий режим: $ACTION"
echo "php.ini бэкап: $BACKUP"

Инструкции:

Сохранить содержимое в /usr/local/bin/gigafiles.

Сделать исполняемым:

chmod +x /usr/local/bin/gigafiles.

Запускать из-под root:

Включить режим для больших файлов:

gigafiles on

Выключить:

gigafiles off

Что делает скрипт:

создаёт бэкап php.ini с временной меткой;
заменяет или добавляет четыре директивы в /etc/php/8.3/fpm/php.ini;
перезагружает php8.3-fpm;
включает/отключает конфигурацию Apache через a2enconf / a2disconf;
проверяет apachectl configtest и — если OK — перезагружает Apache. Если configtest падает, скрипт откатывает php.ini из бэкапа и прекращает (с сообщением об ошибке).

Безопасность/ограничения:

Скрипт предполагает, что файл /etc/apache2/conf-available/nextcloud-large-upload.conf корректен и готов к включению.
Скрипт сохраняет один бэкап при каждом запуске (с временной меткой).
Скрипт не правит другие файлы (pool.d/www.conf и т.п.).

В консоли отработка скрипта выглядит так:

Донаты принимаются на кошельки:

Yoomoney:
4100118091867315

Карта Т-Банк (бывший Тиньков):
2200 7017 2612 2077

Карта Альфа-Банк:
2200 1539 1357 2013