Failų platinimas iš „Google“ disko naudojant „nginx“.

priešistorė

Taip atsitiko, kad man reikėjo kur nors saugoti daugiau nei 1.5 TB duomenų, taip pat suteikti galimybę paprastiems vartotojams juos atsisiųsti per tiesioginę nuorodą. Kadangi tradiciškai tokie atminties kiekiai atitenka VDS, tai nuomos kaina, kuri nelabai yra įtraukta į projekto biudžetą iš kategorijos „nieko veikti“, o iš pirminių duomenų turėjau VPS 400GB SSD, kur net jei aš norėjau, negalėjau įdėti 1.5TB vaizdų be nuostolingo suspaudimo tai pavyks.

Ir tada prisiminiau, kad jei ištrinsiu šlamštą iš Google disko, pvz., programas, kurios veiks tik Windows XP, ir kitus dalykus, kurie perkeliami iš vieno įrenginio į kitą nuo tų laikų, kai internetas buvo visai ne toks greitas ir neribotas ( pavyzdžiui, tos 10-20 virtualios dėžutės versijų vargu ar turėjo jokios kitos vertės, išskyrus nostalgiją), tada viskas turėtų labai tikti. Ne anksčiau pasakyta, nei padaryta. Taigi, peržengus api užklausų skaičiaus ribą (beje, techninė pagalba be problemų padidino užklausų kvotą vienam vartotojui iki 100 10 per 000 sekundžių), duomenys greitai nukeliavo į jo tolesnio diegimo vietą. .

Viskas lyg ir gerai, bet dabar tai reikia perteikti galutiniam vartotojui. Be to, be jokių peradresavimų į kitus išteklius, o taip, kad žmogus tiesiog paspaustų mygtuką „Atsisiųsti“ ir taptų laimingu vertingo failo savininku.

Čia, Dieve, patekau į visokias bėdas. Iš pradžių tai buvo AmPHP scenarijus, bet manęs netenkino jo sukurta apkrova (staigus šuolis pradžioje iki 100% pagrindinio suvartojimo). Tada pasirodė ReactPHP garbanojimo įvynioklis, kuris puikiai atitiko mano norus pagal sunaudotų procesoriaus ciklų skaičių, tačiau visiškai nedavė tokio greičio, kokio norėjau (paaiškėjo, kad galite tiesiog sumažinti skambinimo intervalą curl_multi_select, bet tada mes turime riebumą, panašų į pirmąjį variantą ). Aš net bandžiau parašyti nedidelę paslaugą Rust, ir ji veikė gana greitai (nuostabu, kad ji veikė, atsižvelgiant į mano žinias), bet norėjau daugiau, ir buvo kažkaip sunku ją pritaikyti. Be to, visi šie sprendimai kažkaip keistai buferiavo atsakymą, todėl norėjau kuo tiksliau atsekti momentą, kada failo atsisiuntimas baigiasi.

Apskritai kurį laiką buvo kreivas, bet pavyko. Kol vieną dieną sugalvojau idėją, kuri buvo nepaprasta savo beprotiškumu: nginx teoriškai gali daryti tai, ką noriu, dirbti greitai ir netgi leidžia su konfigūracija visokius iškrypimus. Turime pabandyti – o jei pavyks? Ir po pusės dienos atkaklių paieškų gimė jau kelis mėnesius stabiliai veikęs ir visus mano reikalavimus atitinkantis sprendimas.

NGINX nustatymas

# Первым делом создадим в конфигах нашего сайта отдельную локацию.
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;
}

Trumpą versiją be komentarų galite pamatyti po spoileriu

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

Rašome scenarijų, kaip valdyti visą šią laimę

Pavyzdys bus PHP ir sąmoningai parašytas su minimaliu rinkiniu. Manau, kad kiekvienas, turintis patirties su kita kalba, galės integruoti šią skiltį pagal mano pavyzdį.

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

rezultatai

Apskritai šis metodas leidžia gana lengvai organizuoti failų paskirstymą vartotojams iš bet kurios debesies saugyklos. Taip, net iš telegramos ar VK (su sąlyga, kad failo dydis neviršija leistino šios saugyklos dydžio). Turėjau panašią idėją tai, bet deja susiduriu su failais iki 2GB ir dar neradau būdo ar modulio atsakymams iš aukštyn suklijuoti, o šiam projektui parašyti kažkokius įpakavimus yra neprotingai daug darbo jėgos.

Ačiū už dėmesį. Tikiuosi, kad mano istorija jums buvo bent šiek tiek įdomi ar naudinga.

Šaltinis: www.habr.com

Добавить комментарий