Distribuire file da Google Drive utilizzando nginx

Sfondo

È successo che avevo bisogno di archiviare più di 1.5 TB di dati da qualche parte e anche di fornire agli utenti ordinari la possibilità di scaricarli tramite un collegamento diretto. Poiché tradizionalmente tali quantità di memoria vanno a VDS, il costo del noleggio, che non è molto incluso nel budget del progetto, viene dalla categoria "niente da fare", e dai dati di origine avevo un SSD VPS da 400 GB, dove, anche se volevo, non potevo inserire 1.5 TB di immagini senza compressione senza perdita di dati, ci riuscirà.

E poi mi sono ricordato che se elimino la spazzatura da Google Drive, come i programmi che funzionano solo su Windows XP e altre cose che si spostano da un dispositivo all'altro dai tempi in cui Internet non era affatto così veloce e illimitato ( ad esempio, quelle 10-20 versioni della scatola virtuale difficilmente avrebbero avuto altro valore oltre a quello nostalgico), quindi tutto dovrebbe adattarsi molto bene. Detto fatto. E così, superando il limite del numero di richieste all'API (a proposito, il supporto tecnico senza problemi ha aumentato la quota di richieste per utente a 100 in 10 secondi), i dati sono fluiti rapidamente nel luogo della sua ulteriore distribuzione .

Tutto sembra andare bene, ma ora bisogna trasmetterlo all'utente finale. Inoltre, senza reindirizzamenti ad altre risorse, ma in modo che una persona faccia semplicemente clic sul pulsante "Download" e diventi il ​​felice proprietario del file prezioso.

Qui, perdio, mi sono cacciato in ogni genere di guai. All'inizio era uno script in AmPHP, ma non ero soddisfatto del carico che creava (un brusco salto all'inizio fino al 100% di consumo del core). Poi è entrato in gioco il curl wrapper per ReactPHP, che si adattava perfettamente ai miei desideri in termini di numero di cicli di CPU consumati, ma non ha dato affatto la velocità che volevo (si è scoperto che puoi semplicemente ridurre l'intervallo di chiamata curl_multi_select, ma poi abbiamo una golosità simile alla prima opzione). Ho anche provato a scrivere un piccolo servizio in Rust e ha funzionato abbastanza velocemente (è sorprendente che abbia funzionato, date le mie conoscenze), ma volevo di più ed è stato in qualche modo difficile personalizzarlo. Inoltre, tutte queste soluzioni in qualche modo hanno stranamente bufferizzato la risposta e volevo monitorare il momento in cui il download del file è terminato con la massima precisione.

In generale, per un po 'è stato storto, ma ha funzionato. Finché un giorno mi è venuta un'idea straordinaria nella sua follia: nginx, in teoria, può fare quello che voglio, lavorare velocemente e consentire anche ogni sorta di perversione con la configurazione. Dobbiamo provarci: e se funzionasse? E dopo mezza giornata di ricerca persistente, è nata una soluzione che ha funzionato stabilmente per diversi mesi e ha soddisfatto tutte le mie esigenze.

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

Una versione breve senza commenti può essere vista sotto lo spoiler

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

Stiamo scrivendo una sceneggiatura per gestire tutta questa felicità

L'esempio sarà in PHP e scritto volutamente con un minimo di kit. Penso che chiunque abbia esperienza con qualsiasi altra lingua sarà in grado di integrare questa sezione utilizzando il mio esempio.

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

Risultati di

In generale, questo metodo semplifica l'organizzazione della distribuzione dei file agli utenti da qualsiasi spazio di archiviazione cloud. Sì, anche da Telegram o VK (a condizione che la dimensione del file non superi la dimensione consentita di questo archivio). Ho avuto un'idea simile a questo, ma sfortunatamente mi imbatto in file fino a 2 GB e non ho ancora trovato un metodo o un modulo per incollare le risposte dall'upstream e scrivere una sorta di wrapper per questo progetto è irragionevolmente dispendioso in termini di manodopera.

Grazie per l'attenzione. Spero che la mia storia ti sia stata almeno un po' interessante o utile.

Fonte: habr.com

Aggiungi un commento