Споделяне на файлове от Google Drive с помощта на nginx

праистория

Просто така се случи, че трябваше да съхранявам повече от 1.5 TB данни някъде и също така да осигуря възможност за обикновените потребители да ги изтеглят чрез директна връзка. Тъй като традиционно такива количества памет отиват във VDS, цената на наема на който не е много включена в бюджета на проекта от категорията „няма какво да правя“, и от изходните данни имах VPS 400GB SSD, където, дори ако исках, не можах да сложа 1.5TB изображения без компресия без загуби, ще успее.

И тогава си спомних, че ако изтрия боклуци от Google Drive, като програми, които ще работят само на Windows XP, и други неща, които се преместват от едно устройство на друго от дните, когато интернет не беше толкова бърз, изобщо не беше неограничен ( например тези 10-20 версии на виртуалната кутия е малко вероятно да имат някаква друга стойност освен носталгична), тогава всичко трябва да пасва много добре. Казано, сторено. И така, нарушавайки ограничението за броя на заявките към api (между другото, техническата поддръжка без никакви проблеми увеличи квотата на заявките на потребител до 100 10 за 000 секунди), данните бързо потекоха към мястото на по-нататъшното им разполагане .

Всичко изглежда добре, но сега трябва да се предаде на крайния потребител. Освен това, без никакви пренасочвания към други ресурси, но така, че човек просто да натисне бутона „Изтегляне“ и да стане щастлив собственик на ценния файл.

Ето, за бога, изпаднах в какви ли не неприятности. Първоначално беше скрипт в AmPHP, но не бях доволен от натоварването, което създаде (рязък скок в началото до 100% консумация на ядро). След това влезе в действие обвивката на curl за ReactPHP, която доста се вписваше в желанията ми по отношение на броя на консумираните цикли на процесора, но изобщо не даде скоростта, която исках (оказа се, че можете просто да намалите интервала на извикване curl_multi_select, но тогава имаме лакомия, подобна на първата опция). Дори се опитах да напиша малка услуга в Rust и тя работи доста бързо (изненадващо е, че работи, като се имат предвид познанията ми), но исках повече и беше някак трудно да я персонализирам. Освен това всички тези решения някак си странно буферираха отговора и исках да проследя момента, в който изтеглянето на файла приключи с най-голяма точност.

Общо взето беше криво за малко, но се получи. Докато един ден не ми хрумна идея, която беше забележителна в своята лудост: nginx на теория може да прави каквото искам, да работи бързо и дори да позволява всякакви извращения с конфигурацията. Трябва да опитаме - ами ако работи? И след половин ден упорито търсене се роди решение, което работеше стабилно от няколко месеца и отговаряше на всичките ми изисквания.

Настройване на NGINX

# Первым делом создадим в конфигах нашего сайта отдельную локацию.
location ~* ^/google_drive/(.+)$ {

    # И закроем её от посторонних глаз (рук, ног и прочих частей тела).
    internal;

    # Ограничим пользователям скорость до разумных пределов (я за равноправие).
    limit_rate 1m;

    # А чтоб nginx мог найти сервера google drive укажем ему адрес резолвера.
    resolver 8.8.8.8;

    # Cоберем путь к нашему файлу (мы потом передадим его заголовками).
    set $download_url https://www.googleapis.com/drive/v3/files/$upstream_http_file_id?alt=media;

    # А так же Content-Disposition заголовок, имя файла мы передадим опять же в заголовках.
    set $content_disposition 'attachment; filename="$upstream_http_filename"';

    # Запретим буфферизировать ответ на диск.
    proxy_max_temp_file_size 0;

    # И, что немаловажно, передадим заголовок с токеном (не знаю почему, но в заголовках из $http_upstream токен передать не получилось. Вернее передать получилось, но скорей всего его где-то нужно экранировать, потому что гугл отдает ошибку авторизации).
    proxy_set_header Authorization 'Bearer $1';

    # И все, осталось отправить запрос гуглу по ранее собранному нами адресу.
    proxy_pass $download_url;

    # А чтоб у пользователя при скачивании отобразилось правильное имя файла мы добавим соответствующий заголовок.
    add_header Content-Disposition $content_disposition;

    # Опционально можно поубирать ненужные нам заголовки от гугла.
    proxy_hide_header Content-Disposition;
    proxy_hide_header Alt-Svc;
    proxy_hide_header Expires;
    proxy_hide_header Cache-Control;
    proxy_hide_header Vary;
    proxy_hide_header X-Goog-Hash;
    proxy_hide_header X-GUploader-UploadID;
}

Кратка версия без коментари може да се види под спойлера

location ~* ^/google_drive/(.+)$ {
    internal;
    limit_rate 1m;
    resolver 8.8.8.8;
    
    set $download_url https://www.googleapis.com/drive/v3/files/$upstream_http_file_id?alt=media;
    set $content_disposition 'attachment; filename="$upstream_http_filename"';
    
    proxy_max_temp_file_size 0;
    proxy_set_header Authorization 'Bearer $1';
    proxy_pass $download_url;
    
    add_header Content-Disposition $content_disposition;
    
    proxy_hide_header Content-Disposition;
    proxy_hide_header Alt-Svc;
    proxy_hide_header Expires;
    proxy_hide_header Cache-Control;
    proxy_hide_header Vary;
    proxy_hide_header X-Goog-Hash;
    proxy_hide_header X-GUploader-UploadID;
}

Ние пишем сценарий, за да управляваме цялото това щастие

Примерът ще бъде на PHP и умишлено написан с минимален комплект. Мисля, че всеки, който има опит с друг език, ще може да интегрира този раздел, използвайки моя пример.

<?php

# Токен для Google Drive Api.
define('TOKEN', '*****');

# ID файла на гугл диске
$fileId = 'abcdefghijklmnopqrstuvwxyz1234567890';

# Опционально, но так как мы не передаем никаких данных - почему бы и нет?
http_response_code(204);

# Зададим заголовок c ID файла (в конфигах nginx мы потом получим его как $upstream_http_file_id).
header('File-Id: ' . $fileId);
# И заголовок с именем файла (соответственно $upstream_http_filename).
header('Filename: ' . 'test.zip');
# Внутренний редирект. А еще в адресе мы передадим токен, тот самый, что мы получаем из $1 в nginx.
header('X-Accel-Redirect: ' . rawurlencode('/google_drive/' . TOKEN));

Резултати от

Като цяло, този метод прави доста лесно организирането на разпространението на файлове до потребителите от всяко облачно хранилище. Да, дори от телеграма или VK (при условие, че размерът на файла не надвишава допустимия размер на това хранилище). Имах идея, подобна на това, но за съжаление попадам на файлове до 2GB и все още не съм намерил метод или модул за залепване на отговори от upstream и писането на някакъв вид обвивки за този проект е неоправдано трудоемко.

Благодаря за вниманието. Надявам се моята история да ви е била поне малко интересна или полезна.

Източник: www.habr.com

Добавяне на нов коментар