Настройка nginx для отдачи больших файлов.

12 3
iccup
На сайте с 01.05.2016
Offline
195
1534
Есть файлы размером до 200Мб на сервере которые скачиваются юзерами незатейливым скриптом
<?php
 
$dir=$_SERVER['DOCUMENT_ROOT'];
$_GET['f'] = isset($_GET['f']) ? trim(preg_replace("/[^0-9A-Za-z\.\-\_]/","",$_GET['f'])) : die('404');
$file=$dir.'/papka/videos/'.urldecode($_GET['f']);

download($file);
 
function download($file){
 if (file_exists($file)) {
 
 if (ob_get_level()) {
 ob_end_clean();
 }

$extension = pathinfo($file, PATHINFO_EXTENSION);

$new_name = uniqid().'.'.$extension;

 header('Content-Description: File Transfer');
 header('Content-Type: application/octet-stream');
 header('Content-Disposition: attachment; filename=' .$new_name);
 header('Content-Transfer-Encoding: binary');
 header('Expires: 0');
 header('Cache-Control: must-revalidate');
 header('Pragma: public');
 header('Content-Length: ' . filesize($file));
 readfile($file);
 exit;
 }
 else{
 
 echo 'Can\'t find the file: '.$file;
 }
}

?>

Но есть проблема, некоторые юзеры жалуются что у них не получается скачать (или докачать) файлы. Вероятно идет обрыв из-за плохого интернет соединения. Как бы так настроить nginx, чтобы он учитывал такие нюансы?

user  apache;
worker_processes  4;
worker_rlimit_nofile 16384;
error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  2048;
}


http {
map_hash_bucket_size 10000;
map_hash_max_size 10000;
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

log_format  main  '"$http_x_gt_clientip" - $remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;
sendfile        on;
keepalive_timeout               120;
        keepalive_requests              1000;
        reset_timedout_connection       on;
                client_body_timeout 120;
        tcp_nodelay                     on;
        tcp_nopush                      on;
        send_timeout                    2;
        server_tokens                   off;
        client_body_buffer_size         128k;
      server_names_hash_bucket_size  128;
        types_hash_max_size             2048;
        proxy_buffering                 on; 
        #proxy_buffer_size              512k;
        #proxy_buffers                  32 512k;
                
                proxy_buffers 8 2m;
proxy_buffer_size 10m;
proxy_busy_buffers_size 10m;
        proxy_max_temp_file_size        0;

        proxy_read_timeout              800;
        proxy_connect_timeout           800;
        proxy_send_timeout              800;
        fastcgi_send_timeout           1200;
        fastcgi_read_timeout           1200;

        proxy_cache_valid               10m;
        proxy_cache_key $scheme$proxy_host$request_uri$cookie_US;
        proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=cache:128m inactive=60m max_size=3048m;

        open_file_cache max=200000 inactive=20s;
        open_file_cache_valid           60s;
        open_file_cache_min_uses        2;
        open_file_cache_errors          on;

        gzip                            on;
        gzip_comp_level                 9;
        gzip_min_length                 1000;
        gzip_proxied                    any;
        gzip_vary                       on;
        gzip_static                     on;
        gzip_types
    text/xml application/xml application/atom+xml application/rss+xml application/ xhtml+xml image/svg+xml
    text/javascript application/javascript application/x-javascript
    text/x-json application/json application/x-web-app-manifest+json
    text/css text/plain text/x-component image/jpeg image/gif image/png video/mp4
    video/x-flv application/x-shockwave-flash image/jpg
    font/opentype application/x-font-ttf application/vnd.ms-fontobject
    image/x-icon;
        gzip_disable                    "msie6";
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;

    include /etc/nginx/conf.d/*.conf;
        include /etc/nginx/vhosts/*/*.conf;
server {
        server_name localhost;
 disable_symlinks if_not_owner;
        listen 80;
        listen [::]:80;
        include /etc/nginx/vhosts-includes/*.conf;
        location @fallback {
                error_log /dev/null crit;
                proxy_pass http://127.0.0.1:8080;
                proxy_redirect http://127.0.0.1:8080 /;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                access_log off ;
        }
        proxy_connect_timeout       1000;
    proxy_send_timeout          1000;
    proxy_read_timeout          1000;
    send_timeout                1000;
    }
        client_max_body_size 256m;
...


kimsufi.com ( https://www.kimsufi.com ) очень дешевые и качественные дедики https://clck.ru/gvF9p - антибот, использую уже 3 года.
W1
На сайте с 22.01.2021
Offline
306
#1
iccup :
скачиваются юзерами незатейливым скриптом

А у файлов какие-то специфические расширения что ли? Зачем эти пляски с чтением файла php-скриптом?

iccup :
Вероятно идет обрыв из-за плохого интернет соединения.

Время работы php-скрипта ограничено?

Мой форум - https://webinfo.guru –Там я всегда на связи
iccup
На сайте с 01.05.2016
Offline
195
#2
webinfo #:

А у файлов какие-то специфические расширения что ли? Зачем эти пляски с чтением файла php-скриптом?

Время работы php-скрипта ограничено?

max_execution_time 300

lutskboy
На сайте с 22.11.2013
Offline
178
#3
nginx и сам может отдавать файлы. и докачка будет
LEOnidUKG
На сайте с 25.11.2006
Offline
1748
#4

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

ИЛИ

Создавать ярлыки системные на эти файлы и через них делать скачивание.

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

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

✅ Мой Телеграм канал по SEO, оптимизации сайтов и серверов: https://t.me/leonidukgLIVE ✅ Качественное и рабочее размещение SEO статей СНГ и Бурж: https://getmanylinks.ru/ ✅ Настройка и оптимизация серверов https://getmanyspeed.ru/
iccup
На сайте с 01.05.2016
Offline
195
#5
<?php

// Путь к директории для сохранения временных файлов (относительный путь)
$tempDir = 'videotmp/';

// Путь к оригинальному файлу (относительный путь)
$originalFilePath = 'video/' . $_GET['file'];

// Абсолютный путь к директории временных файлов
$tempDirAbsPath = __DIR__ . '/' . $tempDir;

// Получение расширения оригинального файла
$originalExtension = pathinfo($originalFilePath, PATHINFO_EXTENSION);

// Генерация уникального имени файла с расширением
$tempFileName = uniqid() . '.' . $originalExtension;
$tempFilePath = $tempDirAbsPath . $tempFileName;

// Создание временного файла
if (copy($originalFilePath, $tempFilePath)) {
    // Установка прав доступа для временного файла
    chmod($tempFilePath, 0644);
    
    // Вывод прямой ссылки на временный файл
    $tempFileLink = $tempDir . $tempFileName;
    echo 'Ссылка для скачивания: <a href="' . $tempFileLink . '">Скачать</a>';
} else {
    // Обработка ошибки создания временного файла
    echo 'Ошибка создания временного файла.';
}
?>
Пообщался немного с CHatGPT, и черт побери, работает. Какой же кайф.
LEOnidUKG
На сайте с 25.11.2006
Offline
1748
#6

Функции очистки старых файлов ещё нет.

$tempFileName = uniqid() . '.' . $originalExtension;

Спорный момент, я бы лучше md5 делал из названия файла, чтобы в случаи одинаковых файлов, не плодились дубли.

iccup
На сайте с 01.05.2016
Offline
195
#7
LEOnidUKG #:

Функции очистки старых файлов ещё нет.

Спорный момент, я бы лучше md5 делал из названия файла, чтобы в случаи одинаковых файлов, не плодились дубли.

Есть) Просто я не стал ее указывать, машина предложила такое -

<?php

// Путь к директории с временными файлами
$tempDir = 'путь/к/директории/для/сохранения/временных/файлов/';

// Время в секундах, через которое временные файлы будут удалены (например, 1 час - 3600 секунд)
$expiryTime = 3600;

// Получение текущего времени
$currentTimestamp = time();

// Проверка всех файлов в директории
$files = glob($tempDir . '*');
foreach ($files as $file) {
    // Получение времени создания файла
    $fileCreationTime = filectime($file);
    
    // Проверка, если файл создан раньше, чем указанное время удаления
    if ($currentTimestamp - $fileCreationTime >= $expiryTime) {
        // Удаление файла
        unlink($file);
    }
}

Ну и крон чтобы выполнять это все -

0 * * * * php /путь/к/delete_temp_files.php
По поводу копий, да это так. Учитывая что они будут удаляться каждый час, это нормально. Да, и если допусстим скрипт не будет создавать файл потомучто он как бы уже есть (создан ранее) и оставит как есть, то есть вероятность что пользователь не успеет докачать файл и он удалится по крону.
LEOnidUKG
На сайте с 25.11.2006
Offline
1748
#8
iccup #:


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

Минимизация обращений к диску и операций ввода вывода.

Чтобы файлы не удалялись, которые сейчас скачиваются т.е. актуальные есть функция touch https://www.php.net/manual/en/function.touch.php


$files = glob($tempDir . '*');

Рекомендую:

$files = glob($tempDir . '*' | GLOB_NOSORT  );

Тоже ускорит вывод. Хотя от этой переменной вообще можно избавиться, а сразу в foreach её.

$fileCreationTime = filectime($file);

Тоже бессмысленная переменная.


Вообще конечно код прикольный, но он больше какой-то учебный что-ли.

iccup
На сайте с 01.05.2016
Offline
195
#9

в общем такое предложил он, чтобы не плодить копии -

<?php
$allowedReferer = 'https://domain.com/'; // Замените на адрес вашего сайта
$referer = $_SERVER['HTTP_REFERER'] ?? '';

if (strpos($referer, $allowedReferer) !== 0) {
    // Если реферер не ваш сайт, выводим ошибку и прекращаем выполнение скрипта
    header('HTTP/1.0 403 Forbidden');
    exit('Direct access not allowed.');
}

// Путь к директории для сохранения временных файлов (относительный путь)
$tempDir = 'videostmp/';

// Путь к оригинальному файлу (относительный путь)
$originalFilePath = 'videos/' . $_GET['file'];

// Абсолютный путь к директории временных файлов
$tempDirAbsPath = __DIR__ . '/' . $tempDir;

// Создание директории для временных файлов, если она не существует
if (!is_dir($tempDirAbsPath)) {
    mkdir($tempDirAbsPath, 0755, true);
}

// Получение расширения оригинального файла
$originalExtension = pathinfo($originalFilePath, PATHINFO_EXTENSION);

// Генерация уникального имени файла с расширением
$tempFileName = uniqid() . '.' . $originalExtension;
$tempFilePath = $tempDirAbsPath . $tempFileName;

// Проверяем наличие временного файла
if (file_exists($tempFilePath)) {
    // Обновляем время последнего доступа к файлу
    touch($tempFilePath);
    
    // Выводим прямую ссылку на временный файл
    $tempFileLink = $tempDir . $tempFileName;
    echo 'Download link: <a download href="' . $tempFileLink . '">Download</a>';
} else {
    // Копируем оригинальный файл во временную директорию
    if (copy($originalFilePath, $tempFilePath)) {
        // Установка прав доступа для временного файла
        chmod($tempFilePath, 0644);
        
        // Обновляем время последнего доступа к файлу
        touch($tempFilePath);
        
        // Выводим прямую ссылку на временный файл
        $tempFileLink = $tempDir . $tempFileName;
        echo 'Download link: <a download href="' . $tempFileLink . '">Download</a>';
    } else {
        // Обработка ошибки создания временного файла
        echo 'Error creating temporary file.';
    }
}
?>
iccup
На сайте с 01.05.2016
Offline
195
#10

Ну, и скрипт для удаления -

#!/bin/bash

# Путь к директории с временными файлами
tempDir="/path/to/temp/files"

# Список файлов в директории с временными файлами
tempFiles=$(ls $tempDir)

# Время жизни файла в секундах
fileLifetime=1800

# Текущее время в секундах
currentTime=$(date +%s)

# Цикл по списку файлов
for file in $tempFiles
do
    # Проверяем, является ли файл временным файлом
    if [[ $file == *.tmp.* ]]
    then
        # Получаем дату последнего доступа к файлу
        lastAccessTime=$(stat -c %X $tempDir/$file)

        # Разница между текущим временем и датой последнего доступа к файлу
        timeDiff=$((currentTime - lastAccessTime))

        # Проверяем, превышает ли время жизни файла заданный интервал
        if [ $timeDiff -gt $fileLifetime ]
        then
            # Удаляем файл
            rm $tempDir/$file
        fi
    fi
done
12 3

Авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий