Phân phối tệp từ Google Drive bằng nginx

thời tiền sử

Tình cờ là tôi cần lưu trữ hơn 1.5 TB dữ liệu ở đâu đó và cũng cung cấp khả năng cho người dùng thông thường tải xuống thông qua liên kết trực tiếp. Vì theo truyền thống, lượng bộ nhớ như vậy sẽ được chuyển đến VDS, nên chi phí thuê không được bao gồm nhiều trong ngân sách dự án từ danh mục “không có gì để làm” và từ dữ liệu nguồn, tôi có một ổ SSD VPS 400 GB, ngay cả khi tôi muốn, tôi không thể đặt 1.5TB hình ảnh mà không nén không mất dữ liệu thì nó sẽ thành công.

Và sau đó tôi nhớ rằng nếu tôi xóa rác khỏi Google Drive, chẳng hạn như các chương trình sẽ chỉ chạy trên Windows XP và những thứ khác đã chuyển từ thiết bị này sang thiết bị khác kể từ những ngày Internet không quá nhanh và không giới hạn ( ví dụ: 10-20 phiên bản hộp ảo đó dường như không có giá trị nào khác ngoài sự hoài cổ), thì mọi thứ sẽ rất phù hợp. Không sớm nói hơn làm. Và do đó, vượt qua giới hạn về số lượng yêu cầu đối với api (nhân tiện, bộ phận hỗ trợ kỹ thuật không gặp bất kỳ sự cố nào đã tăng hạn mức yêu cầu cho mỗi người dùng lên 100 trong 10 giây), dữ liệu nhanh chóng được chuyển đến nơi triển khai tiếp theo. .

Mọi thứ tưởng chừng như đã ổn nhưng giờ đây nó cần được truyền tải đến người dùng cuối. Hơn nữa, không có bất kỳ chuyển hướng nào đến các tài nguyên khác mà một người chỉ cần nhấn nút “Tải xuống” và trở thành chủ sở hữu hạnh phúc của tập tin quý giá.

Ở đây, lạy Chúa, tôi đã gặp đủ thứ rắc rối. Lúc đầu, đó là một tập lệnh trong AmPHP, nhưng tôi không hài lòng với tải mà nó tạo ra (một bước nhảy vọt khi bắt đầu đạt mức tiêu thụ 100% lõi). Sau đó, trình bao bọc cuộn tròn cho ReactPHP ra đời, khá phù hợp với mong muốn của tôi về số chu kỳ CPU tiêu thụ, nhưng không mang lại tốc độ như tôi muốn (hóa ra là bạn có thể chỉ cần giảm khoảng thời gian gọi Curl_multi_select nhưng sau đó chúng ta lại có tính háu ăn tương tự như lựa chọn đầu tiên). Tôi thậm chí đã cố gắng viết một dịch vụ nhỏ bằng Rust và nó hoạt động khá nhanh (thật ngạc nhiên là nó hoạt động, dựa trên kiến ​​​​thức của tôi), nhưng tôi muốn nhiều hơn nữa và thật khó để tùy chỉnh nó. Ngoài ra, tất cả các giải pháp này bằng cách nào đó đã đệm phản hồi một cách kỳ lạ và tôi muốn theo dõi thời điểm quá trình tải xuống tệp kết thúc với độ chính xác cao nhất.

Nói chung là nó cong vẹo một thời gian nhưng vẫn dùng được. Cho đến một ngày, tôi nảy ra một ý tưởng cực kỳ điên rồ: về lý thuyết, nginx có thể làm những gì tôi muốn, hoạt động nhanh chóng và thậm chí cho phép đủ loại biến thái về cấu hình. Chúng ta phải thử - nếu nó có tác dụng thì sao? Và sau nửa ngày kiên trì tìm kiếm, một giải pháp đã ra đời, hoạt động ổn định trong vài tháng và đáp ứng mọi yêu cầu của tôi.

Thiết lập 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;
}

Một phiên bản ngắn không có bình luận có thể được nhìn thấy dưới phần 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;
}

Chúng tôi đang viết một kịch bản để quản lý tất cả niềm hạnh phúc này

Ví dụ này sẽ bằng PHP và được viết có chủ ý với mức tối thiểu là bộ công cụ. Tôi nghĩ bất kỳ ai có kinh nghiệm với bất kỳ ngôn ngữ nào khác đều có thể tích hợp phần này bằng ví dụ của tôi.

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

Kết quả

Nhìn chung, phương pháp này giúp việc tổ chức phân phối tệp cho người dùng từ bất kỳ bộ lưu trữ đám mây nào khá dễ dàng. Có, ngay cả từ telegram hoặc VK, (với điều kiện kích thước tệp không vượt quá kích thước cho phép của bộ lưu trữ này). Tôi đã có một ý tưởng tương tự như điều này, nhưng thật không may, tôi gặp các tệp có dung lượng lên tới 2GB và tôi vẫn chưa tìm thấy phương pháp hoặc mô-đun nào để dán các phản hồi từ thượng nguồn và việc viết một số loại trình bao bọc cho dự án này tốn nhiều công sức một cách vô lý.

Cám ơn vì sự quan tâm của bạn. Tôi hy vọng câu chuyện của tôi ít nhất cũng thú vị hoặc hữu ích với bạn một chút.

Nguồn: www.habr.com

Thêm một lời nhận xét