В ходе разработки серверной части сервиса загрузки файлов на Golang родилось отдельное приложение – pavo. В задачи приложения входит загрузка целых файлов, по одному или несколько за раз, кусочная загрузка файла(chunked upload), конвертер изображений. Реализована загрузка данных через
Для установки приложения необходим компилятор Golang. Инструкцию по его установке можно найти на официальном сайте. Так же необходимо настроить переменную окружения
Для примера, как это можно сделать в MacOS:
Репозитории с кодом в сообществе не централизованы. Используются различные системы контроля версий:
Пример установки в MacOS:
Для конвертации изображений на сервер используется ImageMagick:
При первой установке запустите команду в консоли:
При обновлении приложения и зависимых библиотек:
Для того, чтобы посмотреть, как работает приложение с базовым примером, запустим в консоли команду:
Тем самым мы запустили приложение с корневой директорией в указанном через опцию
Типовой ответ сервера при загрузке изображения:
Самый распространённый способ загрузки файлов на сервер – использование форм. В таком случае запрос представляется следующим образом:
В заголовке
Современный подход позволяет отправлять бинарные данные, используя на клиенской стороне запрос типа XHR. Запрос, который увидит сервер, выглядит следующим образом:
Таким способом можно передать за один запрос только один файл, имя которого будет доступно в заголовке
Для загрузки больших файлов на клиентской стороне формируется пачка запросов с частями исходного файла. Пример запроса:
Имя исходного файла и значение cookie по ключу pavo используется для идентификации промежуточного файла с загруженными частями оригинала. Заголовок
Приложение написано на языке Golang. В качестве веб-фреймворка используется Gin. Код разделен на два пакета(upload и attachment) и основое приложение (исполняемый файл). Пакет upload отвечает за загрузку исходных файлов или куска файла. Пакет attachment отвечает за создание конечной директории для хранения файла и его версий, конвертацию изображений, формирование данных. Основное приложение запускает веб-сервер и реализует роль контроллера.
Исходный код с небольшими примера в тестах доступен на github.
Приложение имеет опции запуска
Все запросы на загрузку приложение принимает на адрес
Для всех файлов устанавливается версия по умолчанию original. Для изображений добавляется thumbnail со значением
Для работы в production окружении желательно использовать веб сервер nginx. В задачи веб сервера будет входить приём запросов от клиентов, запись тела во временный файл, авторизация запроса на основном приложении, отправка заголовков исходного запроса на приложение pavo.
Рекомендуемая конфигурация nginx:
От клиента на веб сервер приходит запрос. Nginx дожидается получения всего запроса (эта его особенность не позволяет реализовать полноценный progress bar с проксированием на сервер приложения). После получения запроса тело будет записано во временный файл в директорию, определенную опцией
Перед отправкой запроса на сервер приложения pavo будет произведена авторизация. Для этого используется модуль ngx_http_auth_request_module. Будет сделан подзапрос на location
Далее в заголовки исходного запроса добавляется новая пара, ключ
Сервис задумывался как самостоятельное приложение в инфраструктуре веб проекта, которое берёт на себя роль загрузки и раздачи файлов, конвертации изображений, видео и аудио. С интерфейсом через HTTP Json API.
UPDATE: Исправлены настройки заголовков проксируемых запросов в конфигурации nginx.
multipart/form-data
и загрузка файла в бинарном виде в теле запроса. Для работы в production окружении используется nginx для авторизации и обработки медленных соединений. В качестве клиентской библиотеки можно использовать jQuery File Uploader.Установка
Установка компилятора
Для установки приложения необходим компилятор Golang. Инструкцию по его установке можно найти на официальном сайте. Так же необходимо настроить переменную окружения
$GOPATH
. Для примера, как это можно сделать в MacOS:
- Устанавливаем компилятор;
$ brew install go $ mkdir $HOME/go
- Настраиваем переменную окружения, отредактировав профайл пользователя.
# Add this line in your .zshrc or .bash_profile export GOPATH=$HOME/go export PATH=$PATH:$GOPATH/bin
Установка систем контроля версиями
Репозитории с кодом в сообществе не централизованы. Используются различные системы контроля версий:
Пример установки в MacOS:
$ brew install git mercurial svn bazaar
Установка ImageMagick
Для конвертации изображений на сервер используется ImageMagick:
$ brew install imagemagick
Установка приложения
При первой установке запустите команду в консоли:
$ go get github.com/kavkaz/pavo
При обновлении приложения и зависимых библиотек:
$ go get -u github.com/kavkaz/pavo/...
Быстрый старт
Для того, чтобы посмотреть, как работает приложение с базовым примером, запустим в консоли команду:
$ pavo --storage=$GOPATH/src/github.com/kavkaz/pavo/dummy/root_storage
Тем самым мы запустили приложение с корневой директорией в указанном через опцию
--storage
каталоге. Сервис c базовым примером будет доступен по адресу localhost:9073/example/jfu-basic.html
. Для указания другого хоста и порта используйте консольную опцию --host
.Подробности протокола
Типовой ответ сервера при загрузке изображения:
{
"files": [
{
"dir": "/image/2014/6s/1c5cnx",
"name": "original_user_filename.jpg",
"type": "image",
"versions": {
"original": {
"filename": "original-1qeh.jpg",
"height": 420,
"size": 28057,
"url": "/image/2014/6s/1c5cnx/original-1qeh.jpg",
"width": 300
},
"thumbnail": {
"filename": "thumbnail-1qef.jpg",
"height": 90,
"size": 3566,
"url": "/image/2014/6s/1c5cnx/thumbnail-1qef.jpg",
"width": 120
}
}
}
],
"status": "ok"
}
Самый распространённый способ загрузки файлов на сервер – использование форм. В таком случае запрос представляется следующим образом:
POST /files HTTP/1.1
Content-Length: 21929
Content-Type: multipart/form-data; boundary=----5XhQf4IXV9Q26uHM
------5XhQf4IXV9Q26uHM
Content-Disposition: form-data; name="files[]"; filename="pic.jpg"
Content-Type: image/jpeg
...bytes...
В заголовке
Content-Type
передаётся значение boundary
, которое служит для разделения значений в теле запроса. Таким образом за один запрос можно передать несколько файлов. jQuery File Upload имеет соответствующую опцию, для множественной отправки файлов.Современный подход позволяет отправлять бинарные данные, используя на клиенской стороне запрос типа XHR. Запрос, который увидит сервер, выглядит следующим образом:
POST /files HTTP/1.1
Content-Length: 21744
Content-Disposition: attachment; filename="pic.jpg"
...bytes...
Таким способом можно передать за один запрос только один файл, имя которого будет доступно в заголовке
Content-Disposition
.Для загрузки больших файлов на клиентской стороне формируется пачка запросов с частями исходного файла. Пример запроса:
POST /files HTTP/1.1
Content-Length: 10240
Content-Range: bytes 0-10239/36431
Content-Disposition: attachment; filename="pic.jpg"
Cookie:pavo=377cb76c-2538-40d3-a3d0-13d86d206ba7
...bytes...
Имя исходного файла и значение cookie по ключу pavo используется для идентификации промежуточного файла с загруженными частями оригинала. Заголовок
Content-Range
содержит информацию о том, какую часть файла предаёт клиент и каков размер оригинального файла. Если загружается последний кусок, то сервер завершает процедуру загрузки и формирует ответ с данными о полученном файле и его версиях.Код приложения
Приложение написано на языке Golang. В качестве веб-фреймворка используется Gin. Код разделен на два пакета(upload и attachment) и основое приложение (исполняемый файл). Пакет upload отвечает за загрузку исходных файлов или куска файла. Пакет attachment отвечает за создание конечной директории для хранения файла и его версий, конвертацию изображений, формирование данных. Основное приложение запускает веб-сервер и реализует роль контроллера.
Исходный код с небольшими примера в тестах доступен на github.
Опции приложения
Приложение имеет опции запуска
--host
и --storage
. Указывают host:port
для запуска веб сервера и корневую директорию хранилища соответственно.Все запросы на загрузку приложение принимает на адрес
/files
. В query_string параметре converts
можно передать параметры конвертации для изображений. Например:POST /files?converts={"pic":"400x300"}
Для всех файлов устанавливается версия по умолчанию original. Для изображений добавляется thumbnail со значением
120x90
.Production environment
Для работы в production окружении желательно использовать веб сервер nginx. В задачи веб сервера будет входить приём запросов от клиентов, запись тела во временный файл, авторизация запроса на основном приложении, отправка заголовков исходного запроса на приложение pavo.
Рекомендуемая конфигурация nginx:
server {
listen 80;
server_name pavo.local;
access_log /usr/local/var/log/nginx/pavo/access.log;
error_log /usr/local/var/log/nginx/pavo/error.log notice;
location /auth {
internal;
proxy_method GET;
proxy_set_header Content-Length "";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass_request_body off;
proxy_pass http://localhost:3000/auth/url/in/your/app;
client_max_body_size 0;
}
location /files {
auth_request /auth;
client_body_temp_path /tmp;
client_body_in_file_only on;
client_body_buffer_size 521K;
client_max_body_size 10G;
proxy_pass_request_headers on;
proxy_set_header X-FILE $request_body_file;
proxy_pass_request_body off;
proxy_set_header Content-Length 0;
proxy_pass http://127.0.0.1:9073;
}
location / {
root /Path/To/Root/Of/Storage;
}
}
От клиента на веб сервер приходит запрос. Nginx дожидается получения всего запроса (эта его особенность не позволяет реализовать полноценный progress bar с проксированием на сервер приложения). После получения запроса тело будет записано во временный файл в директорию, определенную опцией
client_body_temp_path
.Перед отправкой запроса на сервер приложения pavo будет произведена авторизация. Для этого используется модуль ngx_http_auth_request_module. Будет сделан подзапрос на location
/auth
, который в свою очередь проксирует заголовки исходного запроса на сервер основного приложения. В случае успешной авторизации сервер должен вернуть пустое тело с кодом статуса ответа 200
.Далее в заголовки исходного запроса добавляется новая пара, ключ
X-File
, а значение – путь до временного файла с телом запроса. И только после этого получившийся запрос (заголовки и пустое тело) отправляется на прилоежение pavo. Оно обрабатывает запрос, сохраняя файлы, и возвращает ответ с данными о загруженных файлах в JSON формате.Заключение
Сервис задумывался как самостоятельное приложение в инфраструктуре веб проекта, которое берёт на себя роль загрузки и раздачи файлов, конвертации изображений, видео и аудио. С интерфейсом через HTTP Json API.
UPDATE: Исправлены настройки заголовков проксируемых запросов в конфигурации nginx.