Distribuindo arquivos do Google Drive usando nginx

Pré-história

Acontece que eu precisava armazenar mais de 1.5 TB de dados em algum lugar e também fornecer aos usuários comuns a capacidade de baixá-los por meio de um link direto. Como tradicionalmente essas quantidades de memória vão para VDS, o custo do aluguel que não está muito incluído no orçamento do projeto da categoria “nada para fazer”, e pelos dados iniciais eu tinha um SSD VPS 400GB, onde, mesmo que eu queria, não consegui colocar 1.5 TB de imagens sem compactação sem perdas, ele terá sucesso.

E então me lembrei que se eu excluir lixo do Google Drive, como programas que só rodam no Windows XP, e outras coisas que foram movidas de um dispositivo para outro desde os dias em que a Internet não era tão rápida, não ilimitada ( por exemplo, era improvável que aquelas versões 10-20 da caixa virtual tivessem qualquer valor além de nostálgico), então tudo deveria se encaixar muito bem. Dito e feito. E assim, ultrapassando o limite de solicitações à API (aliás, o suporte técnico sem problemas aumentou a cota de solicitações por usuário para 100 em 10 segundos), os dados fluíram rapidamente para o local de sua posterior implantação .

Tudo parece estar bem, mas agora precisa ser transmitido ao usuário final. Além disso, sem nenhum redirecionamento para outros recursos, mas para que uma pessoa simplesmente pressione o botão “Download” e se torne o feliz proprietário do precioso arquivo.

Aqui, por Deus, tive todos os tipos de problemas. No início era um script em AmPHP, mas não fiquei satisfeito com a carga que ele criou (um salto acentuado no início para 100% de consumo do núcleo). Então o wrapper curl para ReactPHP entrou em ação, o que se encaixou perfeitamente nos meus desejos em termos de número de ciclos de CPU consumidos, mas não deu a velocidade que eu queria (descobri que você pode simplesmente reduzir o intervalo de chamada curl_multi_select, mas aí temos uma gula parecida com a primeira opção). Até tentei escrever um pequeno serviço em Rust e funcionou bem rápido (é surpreendente que tenha funcionado, dado o meu conhecimento), mas eu queria mais e foi um tanto difícil personalizá-lo. Além disso, todas essas soluções de alguma forma amorteceram estranhamente a resposta, e eu queria rastrear o momento em que o download do arquivo terminava com a maior precisão.

Em geral ficou torto por um tempo, mas funcionou. Até que um dia tive uma ideia que era notável por sua loucura: o nginx, em teoria, pode fazer o que eu quiser, trabalhar rápido e até permitir todo tipo de perversões na configuração. Temos que tentar – e se funcionar? E depois de meio dia de busca persistente, nasceu uma solução que funcionava de forma estável há vários meses e atendia a todos os meus requisitos.

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

Uma versão curta sem comentários pode ser vista abaixo do 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 escrevendo um roteiro para administrar toda essa felicidade

O exemplo será em PHP e escrito deliberadamente com um mínimo de kit. Acho que qualquer pessoa que tenha experiência com qualquer outra linguagem poderá integrar esta seção usando 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

Em geral, esse método facilita bastante a organização da distribuição de arquivos aos usuários de qualquer armazenamento em nuvem. Sim, mesmo de telegrama ou VK, (desde que o tamanho do arquivo não ultrapasse o tamanho permitido deste armazenamento). tive uma ideia parecida com este, mas infelizmente me deparei com arquivos de até 2 GB e ainda não encontrei um método ou módulo para colar respostas do upstream, e escrever algum tipo de wrapper para este projeto é excessivamente trabalhoso.

Obrigado pela sua atenção. Espero que minha história tenha sido pelo menos um pouco interessante ou útil para você.

Fonte: habr.com

Adicionar um comentário