Préhistoire
Il se trouve que j'avais besoin de stocker plus de 1.5 To de données quelque part et de permettre aux utilisateurs ordinaires de les télécharger via un lien direct. Étant donné que traditionnellement, de telles quantités de mémoire vont au VDS, le coût de location, qui n'est pas très inclus dans le budget du projet, de la catégorie « rien à faire », et à partir des données sources, j'avais un SSD VPS de 400 Go, où, même si je je le voulais, je ne pouvais pas mettre 1.5 To d'images sans compression sans perte, ça réussira.
Et puis je me suis souvenu que si vous supprimez les fichiers inutiles de Google Drive, comme les programmes qui ne fonctionnent que sur Windows XP, et autres fichiers transférés d'un disque dur à l'autre depuis l'époque où Internet n'était ni aussi rapide ni aussi illimité (par exemple, les 10 à 20 versions de VirtualBox n'avaient plus grand intérêt, si ce n'est pour la nostalgie), tout devait s'installer sans problème. Aussitôt dit, aussitôt fait. Et ainsi, la limite de requêtes API étant dépassée (d'ailleurs, le support technique a facilement augmenté le quota de requêtes par utilisateur à 10 000 en moins de 100 secondes), les données ont rapidement atteint leur destination finale.
Tout semble aller bien, mais il faut maintenant que cela soit transmis à l'utilisateur final. De plus, sans aucune redirection vers d'autres ressources, mais pour qu'une personne appuie simplement sur le bouton « Télécharger » et devienne l'heureux propriétaire du fichier précieux.
Ici, par Dieu, j'ai eu toutes sortes de problèmes. Au début c'était un script en AmPHP, mais je n'étais pas satisfait de la charge qu'il créait (un bond brutal au départ jusqu'à 100% de consommation core). Ensuite, le wrapper curl pour ReactPHP est entré en jeu, qui correspondait tout à fait à mes souhaits en termes de nombre de cycles CPU consommés, mais ne donnait pas du tout la vitesse que je voulais (il s'est avéré que vous pouvez simplement réduire l'intervalle d'appel curl_multi_select, mais nous avons alors une gourmandise similaire à la première option ). J'ai même essayé d'écrire un petit service dans Rust, et cela a fonctionné assez rapidement (c'est surprenant que cela ait fonctionné, compte tenu de mes connaissances), mais j'en voulais plus, et c'était en quelque sorte difficile de le personnaliser. De plus, toutes ces solutions tamponnaient étrangement la réponse, et je voulais suivre le moment où le téléchargement du fichier se terminait avec la plus grande précision.
En général, c'était tordu pendant un moment, mais ça a fonctionné. Jusqu'au jour où j'ai eu une idée remarquable par sa folie : nginx, en théorie, peut faire ce que je veux, travailler rapidement et même permettre toutes sortes de perversions avec la configuration. Nous devons essayer – et si cela fonctionnait ? Et après une demi-journée de recherche persistante, est née une solution qui fonctionnait de manière stable depuis plusieurs mois et répondait à toutes mes exigences.
Configuration de 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;
}
Une version courte sans commentaires est visible sous le 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;
}Nous écrivons un scénario pour gérer tout ce bonheur
L'exemple sera en PHP et volontairement écrit avec un minimum de kit. Je pense que toute personne ayant de l'expérience avec une autre langue pourra intégrer cette section en utilisant mon exemple.
<?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));Les résultats de
En général, cette méthode permet d'organiser assez facilement la distribution de fichiers aux utilisateurs à partir de n'importe quel stockage cloud. Oui, même à partir d'un télégramme ou de VK (à condition que la taille du fichier ne dépasse pas la taille autorisée pour ce stockage). J'ai eu une idée similaire à cette, mais malheureusement, je tombe sur des fichiers allant jusqu'à 2 Go, et je n'ai pas encore trouvé de méthode ou de module pour coller les réponses en amont, et écrire une sorte de wrappers pour ce projet demande trop de travail.
Merci pour votre attention. J'espère que mon histoire vous a été au moins un peu intéressante ou utile.
Source: habr.com
