Distribución de ficheiros desde Google Drive usando nginx

prehistoria

Daba a casualidade de que necesitaba almacenar máis de 1.5 TB de datos nalgún lugar e tamén ofrecer a posibilidade de que os usuarios comúns puidesen descargalos a través dunha ligazón directa. Dado que tradicionalmente tales cantidades de memoria van para VDS, o custo do aluguer que non está moi incluído no orzamento do proxecto da categoría "nada que facer", e dos datos de orixe tiña un SSD VPS de 400 GB, onde, aínda que eu quería, non podería poñer 1.5 TB de imaxes sen compresión sen perdas, terá éxito.

E entón lembreime de que se borro lixo de Google Drive, como programas que só se executarán en Windows XP, e outras cousas que se foron movendo dun dispositivo a outro dende os tempos nos que Internet non era tan rápido para nada non era ilimitado ( por exemplo, é improbable que esas versións 10-20 da caixa virtual tivesen ningún valor que non fose nostálxico), entón todo debería encaixar moi ben. Nada máis dicir que feito. E así, superando o límite no número de solicitudes á API (por certo, o soporte técnico aumentou sen ningún problema a cota de solicitudes por usuario a 100 en 10 segundos), os datos fluíron rapidamente ao lugar da súa posterior implantación. .

Todo parece estar ben, pero agora hai que transmitilo ao usuario final. Ademais, sen ningunha redirección a outros recursos, pero para que unha persoa simplemente prema o botón "Descargar" e convértese no feliz propietario do ficheiro atesorado.

Aquí, por Deus, entrei en todo tipo de problemas. Ao principio era un script en AmPHP, pero non estaba satisfeito coa carga que creou (un salto brusco ao principio ao 100% de consumo do núcleo). Entón entrou en xogo o envoltorio de rizos para ReactPHP, que encaixaba bastante nos meus desexos en canto ao número de ciclos de CPU consumidos, pero non deu a velocidade en absoluto o que quería (resultou que simplemente pode reducir o intervalo de chamadas). curl_multi_select, pero despois temos unha gula similar á primeira opción). Incluso tentei escribir un pequeno servizo en Rust, e funcionou bastante rápido (é sorprendente que funcionase, tendo en conta os meus coñecementos), pero quería máis, e era difícil personalizar. Ademais, todas estas solucións amortiguaron de forma estraña a resposta e quería seguir o momento en que a descarga do ficheiro rematou coa maior precisión.

En xeral, estivo torto durante un tempo, pero funcionou. Ata que un día ocorréuseme unha idea que era notable pola súa tolemia: nginx, en teoría, pode facer o que queira, traballar rapidamente e mesmo permitir todo tipo de perversións coa configuración. Temos que probar, e se funciona? E despois de medio día de busca persistente, naceu unha solución que levaba varios meses funcionando de forma estable e cumpriu todos os meus requisitos.

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

Unha versión curta sen comentarios pódese ver baixo o 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;
}

Estamos escribindo un guión para xestionar toda esta felicidade

O exemplo estará en PHP e escrito deliberadamente cun mínimo de kit. Creo que calquera que teña experiencia con calquera outro idioma poderá integrar esta sección usando o meu exemplo.

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

Resultados de

En xeral, este método fai que sexa moi sinxelo organizar a distribución de ficheiros aos usuarios desde calquera almacenamento na nube. Si, incluso desde telegram ou VK, (sempre que o tamaño do ficheiro non exceda o tamaño permitido deste almacenamento). Tiven unha idea semellante a isto, pero lamentablemente atopo con ficheiros de ata 2 GB, e aínda non atopei un método ou módulo para pegar as respostas de upstream, e escribir algún tipo de envoltorios para este proxecto é moi laborioso.

Grazas pola súa atención. Espero que a miña historia fose polo menos un pouco interesante ou útil para vostede.

Fonte: www.habr.com

Engadir un comentario