Раздаём файлы з Google Drive з дапамогай nginx.

перадгісторыя

Так ужо здарылася, што трэба мне было дзесьці захоўваць больш за 1.5тб дадзеных, ды яшчэ і забяспечыць магчымасць запампоўкі іх звычайнымі карыстальнікамі па прамой спасылцы. Паколькі традыцыйна такія аб'ёмы памяці ідуць ужо на VDS, кошт арэнды якіх не занадта ўкладваецца ў бюджэт праекта з катэгорыі "ад няма чаго рабіць", а з зыходных дадзеных у мяне быў VPS 400GB SSD, куды пры ўсім жаданні 1.5тб карцінак без lossless сціску змясціць не атрымаецца.

І тут я ўспомніў пра тое, што калі выдаліць з гугл-дыска халусце, накшталт праграм, якія запусцяцца толькі на Windows XP, і іншыя рэчы, якія вандруюць у мяне з носьбіта на носьбіт з тых часоў, калі інтэрнэт быў не такім хуткім і зусім не безлімітным (напрыклад, тыя 10-20 версій virtual box ці наўрад мелі нейкую каштоўнасць, акрамя настальгічнай), тое ўсё павінна вельмі нават добра змясціцца. Сказана - зроблена. І вось, прабіваючыся праз ліміт на колькасць запытаў да api (дарэчы, тэхпадтрымка без праблем павялічыла квоту запытаў на карыстача за 100 секунд да 10 000) дадзеныя жвава пацяклі ў месца сваёй далейшай дыслакацыі.

Усё быццам бы і добра, але зараз гэта трэба данесці да канчатковага карыстальніка. Ды яшчэ і без усялякіх там рэдырэктаў на іншыя рэсурсы, а каб чалавек проста націснуў кнопачку "Download" і стаў шчаслівым уладальнікам запаветнага файла.

Тут я, далібог, пусціўся ва ўсе цяжкія. Спачатку гэта быў скрыпт на AmPHP, але мяне не задаволіла ствараная ім нагрузка (рэзкі скачак на старце да 100% спажывання ядра). Потым у справу пайшоў уперпер curl для ReactPHP, які цалкам упісваўся ў мае пажаданні па якая едзе колькасці тактаў CPU, але даваў хуткасць зусім не такую, як мне жадалася (аказалася, што можна проста паменшыць інтэрвал выкліку 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));

Вынікі

У цэлым гэты спосаб дазваляе даволі лёгка арганізаваць раздачу файлаў карыстальнікам з любога хмарнага сховішча. Ды хоць з telegram або VK, (пры ўмове, што памер файла не перавышае дапушчальны памер у гэтага сховішча). У мяне была ідэя, падобная гэтай, але нажаль у мяне трапляюцца файлы аж да 2гб, а спосабу або модуля для злепвання адказаў з upstream я пакуль не знайшоў, пісаць жа нейкія врапперы для гэтага праекту неапраўдана працаёмка.

Дзякуй за ўвагу. Спадзяюся, мая гісторыя была хоць крыху вам цікавая ці карысная.

Крыніца: habr.com

Дадаць каментар