Distribuce souborů z Disku Google pomocí nginx

pravěk

Stalo se tak, že jsem potřeboval někde uložit více než 1.5 TB dat a také poskytnout možnost pro běžné uživatele stáhnout si je přes přímý odkaz. Vzhledem k tomu, že tradičně takové objemy paměti jdou do VDS, náklady na pronájem, který není příliš zahrnut v rozpočtu projektu z kategorie „nic na práci“, a ze zdrojových dat jsem měl VPS 400GB SSD, kde, i když jsem chtěl, nemohl jsem dát 1.5 TB obrázků bez bezztrátové komprese to uspěje.

A pak jsem si vzpomněl, že když smažu z Disku Google nevyžádané věci, jako jsou programy, které poběží pouze na Windows XP, a další věci, které se od dob, kdy internet nebyl tak rychlý, vůbec neomezují ( například těch 10-20 verzí virtuální krabice pravděpodobně nebude mít jinou hodnotu než nostalgickou), pak by vše mělo velmi dobře sedět. Sotva řečeno, než uděláno. A tak po prolomení limitu počtu požadavků na api (mimochodem technická podpora bez problémů zvýšila kvótu požadavků na uživatele na 100 10 za 000 sekund), data rychle proudila na místo jeho dalšího nasazení .

Vše se zdá být dobré, ale nyní je potřeba to předat koncovému uživateli. Navíc bez přesměrování na jiné zdroje, ale tak, že člověk jednoduše stiskne tlačítko „Stáhnout“ a stane se šťastným vlastníkem cenného souboru.

Tady jsem se, proboha, dostal do nejrůznějších problémů. Nejprve to byl skript v AmPHP, ale nebyl jsem spokojen se zátěží, kterou vytvořil (prudký skok při startu na 100% spotřebu jádra). Pak přišel na řadu curl wrapper pro ReactPHP, který docela zapadal do mých přání co do počtu spotřebovaných cyklů CPU, ale nedával rychlost vůbec takovou, jakou jsem chtěl (ukázalo se, že interval volání lze jednoduše zkrátit curl_multi_select, ale pak tu máme obžerství podobné první možnosti ). Dokonce jsem se pokusil napsat malou službu v Rustu a fungovalo to docela rychle (je překvapivé, že to fungovalo, vzhledem k mým znalostem), ale chtěl jsem víc a bylo nějak obtížné ji přizpůsobit. Všechna tato řešení navíc nějak zvláštně ukládala do bufferu odezvu a chtěl jsem s největší přesností sledovat okamžik, kdy stahování souboru skončilo.

Obecně to chvíli křivilo, ale šlo to. Až jsem jednoho dne přišel s nápadem, který byl pozoruhodný ve své šílenosti: nginx si teoreticky může dělat, co chci, pracovat rychle a dokonce dovolit nejrůznější zvrácenosti s konfigurací. Musíme to zkusit – co když to funguje? A po půl dni vytrvalého hledání se zrodilo řešení, které fungovalo stabilně několik měsíců a splňovalo všechny mé požadavky.

Nastavení 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;
}

Krátká verze bez komentáře je k vidění pod spoilerem

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;
}

Píšeme scénář, abychom všechno to štěstí zvládli

Příklad bude v PHP a záměrně napsaný s minimem sady. Myslím, že každý, kdo má zkušenosti s jakýmkoli jiným jazykem, bude schopen integrovat tuto sekci pomocí mého příkladu.

<?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));

Výsledky

Tato metoda obecně usnadňuje organizaci distribuce souborů uživatelům z jakéhokoli cloudového úložiště. Ano, i z telegramu nebo VK (za předpokladu, že velikost souboru nepřesáhne povolenou velikost tohoto úložiště). Měl jsem podobný nápad jako tohle, ale bohužel narážím na soubory do 2GB a zatím jsem nenašel metodu ani modul pro lepení odpovědí z upstreamu a psaní jakýchsi wrapperů pro tento projekt je nepřiměřeně pracné.

Děkuji za pozornost. Doufám, že pro vás byl můj příběh alespoň trochu zajímavý nebo užitečný.

Zdroj: www.habr.com

Přidat komentář