Статьи

Создание компонента аватара пользователя с Node.js и TransloadIt

В первой части этой серии мы рассмотрели TransloadIt – сервис обработки файлов, который специализируется на обработке изображений, видео и аудио. Если вы еще не читали его, я бы настоятельно рекомендовал вам сделать это сейчас, поскольку он охватывает множество базовых концепций, которые вам необходимо понять, чтобы следовать этой части.

Но хватит рассуждений, фона и теории – давайте рассмотрим практический пример того, как использовать сервис для обработки некоторых изображений в вашем собственном приложении.

В этом руководстве мы собираемся реализовать функцию фото профиля пользователя для произвольного приложения. Мы будем использовать TransloadIt, чтобы сделать следующее:

  1. Перехватить загрузку файла, загружая его не в ваше приложение, а на их серверы.
  2. Выполните проверку файла на стороне сервера, чтобы убедиться, что он соответствует определенным критериям, например, действительно ли это изображение.
  3. Создайте несколько различных производных – например, размеров – загруженного изображения, таких как миниатюры различных размеров, а также «среднюю» и «большую» версии для страниц профиля пользователя.
  4. Перенесите полученные производные в корзину Amazon S3.
  5. Отобразите миниатюру недавно загруженного изображения в нашем приложении.
  6. Используйте информацию, возвращенную из TransloadIt, чтобы сообщить нашему приложению, где найти результирующие изображения, чтобы мы могли сохранить ссылку на них в пользовательских записях.

Первым шагом является создание некоторых шаблонов, содержащих инструкции по сборке.

Начало работы с шаблонами

Шаблон содержит инструкции по сборке в формате JSON. Запустите ваш любимый текстовый редактор, начните JSON:

{ } 

… И давайте погрузимся.

Фильтрация файлов

Сначала мы добавим шаг, который использует робот / file / filter для проверки типа MIME загруженного файла, чтобы убедиться, что это изображение. Добавьте следующее в ваш пустой документ JSON:

 "steps": "files": { "robot": "/file/filter", "accepts": [ [ "${file.mime}", "regex", "image" ] ], "error_on_decline": true }, 

Давайте разберемся с этим.

Мы начинаем с шага, идентифицируемого ключевыми files . Вы можете называть это как хотите, но files имеют смысл в большинстве случаев.

Далее мы говорим TransloadIt использовать /file/filter/ robot, который используется для выполнения некоторых проверок входящих файлов. В этом случае мы говорим, что хотим принять; мы просим его извлечь MIME-тип файла и запустить для него регулярное выражение ( image ).

В инструкциях TransloadIt переменная обозначается знаком доллара и фигурными скобками ${} . При указании регулярного выражения вам нужно только указать его основную часть.

Если тест, который мы устанавливаем, проваливается, параметр error_on_decline обеспечивает error_on_decline ошибки, а не error_on_decline к следующему шагу.

Другими словами, мы говорим TransloadIt отклонять любые файлы, которые не являются изображениями.

Добавление шагов изменения размера

Теперь давайте создадим еще три шага, каждый из которых будет выполнять одно и то же – создание производного (то есть определенного размера) входящих изображений.

Мы можем называть шаги как угодно, поэтому будем использовать имена, которые обеспечивают некоторый контекст для производных – medium , large и thumbnail .

Давайте определим первый из этих шагов:

 "medium": { "use": ":original", "robot": "/image/resize", "width": 300, "height": 200, "resize_strategy": "fit" }, 

Здесь мы определяем шаг с именем medium , который использует робота /image/resize . Для этого требуется ряд параметров, многие из которых являются необязательными, которые описаны здесь .

Параметр use указывает ему изменить размер исходного файла.

В этом случае мы предоставляем требуемые размеры – 300 на 200 пикселей, а также указываем стратегию изменения размера. Доступные стратегии изменения размера задокументированы здесь , но, по существу, fit гарантирует, что размер изображения изменяется в соответствии с указанными размерами, сохраняя при этом соотношение сторон.

large шаг практически идентичен:

 "large": { "use": ":original", "robot": "/image/resize", "width": 480, "height": 320, "resize_strategy": "fit" }, 

Затем шаг thumbnail :

 "thumbnail": { "use": ":original", "robot": "/image/resize", "width": 80, "height": 80, "resize_strategy": "crop" }, 

На этот раз мы используем стратегию crop , чтобы в итоге получилось идеально квадратное изображение.

Чтобы сделать этот процесс более эффективным, нет причин, по которым вы не можете установить параметр use чтобы сообщить TransloadIt, что база миниатюр будет основана на ваших уже обработанных больших или средних версиях.

На этом этапе мы убедились, что имеем дело с изображением, и изменили его размер три раза, чтобы создать три отдельных производных изображения. Далее мы расскажем TransloadIt, что делать с вновь созданными производными.

экспорт

Как отмечалось ранее, Transloadit не будет долго хранить наши файлы – хостинг – это не то, чем занимается служба, поэтому нам нужно переместить файлы куда-нибудь более постоянные.

Мы собираемся использовать робот экспорта файлов /s3/store для загрузки файлов в корзину Amazon S3.

Вот как вы можете настроить этот шаг:

 "export": { "use": [ "medium", "large", "thumbnail" ], "robot": "/s3/store", "path": "users/profiles/${fields.username}_${previous_step.name}.${file.ext}", "key": "YOUR-S3-AUTH-KEY", "secret": "YOUR-S3-AUTH-SECRET", "bucket": "YOUR-BUCKET-NAME" } 

Вам нужно будет заменить свои учетные данные S3 и имя корзины своими собственными.

Это немного сложнее, чем наши предыдущие шаги, поэтому давайте разберем его.

Параметр use указывает роботу выполнить этот шаг для каждого из наших изображений с измененным размером, создавая три файла на S3 для каждой загрузки. Как видите, medium , large и thumbnail соответствуют идентификаторам наших трех предыдущих шагов.

Затем мы указываем ключ – термин S3 для пути – используемый для хранения полученных файлов, используя значение конфигурации path . Это, в сочетании с полностью определенным доменным именем группы, позже становится URI получающихся производных изображений.

В приведенном выше примере мы используем следующий шаблон:

 users/profiles/${fields.username}_${previous_step.name}.${file.ext} 

Этот шаблон начинается с префикса пути с users/profiles/ , а затем использует значение скрытого поля формы с именем username которое мы вскоре определим. Затем он объединяет это с ключом, который определяет предыдущий шаг, который является именем наших производных. Наконец, он добавляет расширение исходного файла, которое доступно через переменную ${file.ext} .

Это довольно громко, так что, возможно, лучше всего это проиллюстрировать на примере. Учитывая имя пользователя bob , этот шаблон будет производить следующие три пути:

 users/profiles/bob_medium.jpg users/profiles/bob_large.jpg users/profiles/bob_thumbnail.jpg 

Вы можете использовать всевозможные стратегии именования, выбирая и изменяя доступные вам переменные. Для примера рассмотрим следующую схему:

 users/profiles/${fields.username}${file.meta.width}x${file.meta.width}.${file.ext} 

Это динамически создает имя файла, объединяя имя пользователя, ширину и высоту результирующего файла и, наконец, расширение файла. Это приведет к чему-то вроде этого:

 users/profiles/bob480x320.jpg 

Обратите внимание, что если изображение меньше целевого размера производного, эти значения будут отражать конечное изображение, а не настроенные размеры.

Чтобы просто использовать оригинальное имя файла:

 ${file.name} 

Чтобы обеспечить уникальность, следующая переменная предоставляет уникальный 32-символьный префикс:

 ${unique_prefix} 

Полный список доступных переменных см. В разделе документации по переменным сборки .

Загрузка шаблонов

Собрав все эти шаги вместе, наши инструкции по сборке – которые составляют наш шаблон – выглядят так:

 { "steps": { "files": { "robot": "/file/filter", "accepts": [ [ "${file.mime}", "regex", "image" ] ], "error_on_decline": true }, "medium": { "use": ":original", "robot": "/image/resize", "width": 300, "height": 200, "resize_strategy": "fit" }, "large": { "use": ":original", "robot": "/image/resize", "width": 480, "height": 320, "resize_strategy": "fit" }, "thumbnail": { "use": ":original", "robot": "/image/resize", "width": 80, "height": 80, "resize_strategy": "crop" }, "export": { "use": [ "medium", "large", "thumbnail" ], "robot": "/s3/store", "path": "users/profiles/${fields.username}_${previous_step.name}.${file.ext}", "key": "YOUR-S3-AUTH-KEY", "secret": "YOUR-S3-AUTH-SECRET", "bucket": "YOUR-BUCKET-NAME" } } } 

Введите свои собственные учетные данные S3 в соответствующем месте, и тогда мы будем готовы загрузить шаблон в TransloadIt.

Вы найдете JSON выше в репозитории примеров кода, который сопровождает это руководство , в файле с именем template.json .

Если вы еще не создали учетную запись с TransloadIt, вам нужно сделать это сейчас .

Вам нужно будет войти в систему; затем перейдите на свою панель инструментов (Моя учетная запись). В разделе « Интеграции» на левой боковой панели выберите « Шаблоны» . Затем нажмите кнопку « Новый» в правом верхнем углу.

Вам будет предложено user_avatars имя для идентификации вашего шаблона – что-то вроде user_avatars должно user_avatars . Затем вставьте вышеуказанный JSON (который вы также найдете в корне репозитория, прилагаемого к этой статье) в – убедитесь, что вы заменили фиктивные значения S3 своими собственными – и нажмите « Сохранить» .

Если вы предпочитаете использовать альтернативный механизм хранения, такой как (S) FTP или Rackspace Cloud Files, вы найдете соответствующую документацию здесь – просто измените последний шаг соответствующим образом.

Вы вернетесь к основному файлу шаблонов и заметите, что недавно созданному шаблону был присвоен хэш в качестве уникального идентификатора. Запишите это, потому что это понадобится вам позже.

После этого давайте посмотрим, как вы используете TransloadIt из своего приложения.

Пример приложения

Вы найдете пример приложения для сопровождения этого урока на Github .

Чтобы запустить его, вам необходимо убедиться, что у вас установлены следующие предварительные требования:

  • Node.js
  • НПМ
  • MongoDB
  • Беседка

Пользователи Vagrant найдут Vagrantfile в репозитории для создания виртуальной машины, которая включает все перечисленные зависимости.

По сути, это простое приложение Express . Есть несколько вещей, которые мы не будем здесь освещать для краткости:

  • Он использует модуль config для хранения конфигурации приложения в файле .yaml .
  • Он использует Mongoose с MongoDB для определения пользовательской модели.
  • Он использует Passport с локальной стратегией для обеспечения простого механизма аутентификации.
  • Он обеспечивает промежуточное программное обеспечение для безопасного хэширования паролей.
  • Он включает в себя простое промежуточное программное обеспечение для ограничения определенных маршрутов только для аутентифицированных пользователей.
  • Он использует Handlebars вместе с пакетом handlebars-layouts для обработки шаблонов.

Для начала клонируйте приложение, затем установите зависимости:

 npm install bower install 

В приложении есть пара элементов, которые стоит кратко изложить.

Вот определение схемы для модели User :

 var userSchema = mongoose.Schema({ username : { type: String, required: true, unique: true }, email : { type: String, required: true, unique: true }, password : { type: String, required: true }, avatar : { type: mongoose.Schema.Types.Mixed, required: false } }); 

Обратите внимание, как мы включаем поле avatar типа Mixed . Это позволит нам указать аватар в качестве хеша, например:

 user.avatar = { thumbnail : 'http://your.bucket.name.aws.amazon.com/user/profile/bob_thumbnail.jpg', medium : 'http://your.bucket.name.aws.amazon.com/user/profile/bob_medium.jpg', large : 'http://your.bucket.name.aws.amazon.com/user/profile/bob_large.jpg' }; 

Теперь, когда базовая структура создана, давайте посмотрим на плагин TransloadIt jQuery.

Плагин jQuery

Самый простой способ интеграции с TransloadIt на стороне клиента – использовать официальный плагин jQuery , хотя есть и другие альтернативы, как мы увидим позже в этой статье.

Последняя версия плагина доступна по следующему URL:

 https://assets.transloadit.com/js/jquery.transloadit2-latest.js 

Минимальная интеграция включает в себя следующие шаги:

  • Вы привязываете плагин к своей форме
  • Плагин «захватывает» отправку формы, отправляя файлы напрямую в Transloadit
  • Плагин ждет, пока файлы не будут загружены и обработаны
  • Transloadit возвращает объект JSON с результатами, который также будет включать URL-адреса вновь созданных файлов
  • Он создает скрытый элемент textarea, содержащий JSON из Transloadit
  • Форма подана в вашу заявку

Вот очень простой пример инициализации плагина с указанием использовать шаблон:

 $(function() { $('#upload-form').transloadit({ wait: true, params: { auth: { key: 'YOUR-AUTH-KEY' }, template_id: 'YOUR-TEMPLATE-ID' } }); }); 

Однако, как мы отмечали в предыдущей части, предоставление учетных данных для аутентификации в коде на стороне клиента – плохая идея. Вместо этого мы будем использовать подпись.

Подписи

Подписи являются более безопасной альтернативой использованию токенов аутентификации, хотя они требуют некоторой работы на стороне сервера.

По сути, использование подписей требует, чтобы вместо отправки набора инструкций в TransloadIt из вашего клиентского приложения вы закодировали инструкции и зашифровали их, используя алгоритм HMAC вместе с вашим личным ключом аутентификации. В результате создается временный токен, т. Е. Подпись, которая ограничена конкретной комбинацией инструкций. Поскольку это временно, то если этот токен будет скомпрометирован, он очень быстро станет бесполезным.

Вам не нужно беспокоиться о тонкостях генерации подписи самостоятельно, поскольку мы можем использовать стороннюю библиотеку для управления процессом. Если вы используете Node.js, об этом позаботится официальный SDK .

Чтобы установить библиотеку:

 npm install transloadit --save 

Вам понадобятся ключ аутентификации и секрет аутентификации, которые вы можете получить в разделе «Учетные данные API » на сайте TransloadIt. Поместите их в соответствующую часть config\default.yaml .

Вам нужно будет создать файл конфигурации по умолчанию, переименовав или скопировав RENAME_THIS_TO_default.yaml в default.yaml .

Теперь создайте экземпляр класса TransloaditClient , предоставив ему ваши данные аутентификации:

 var TransloaditClient = require('transloadit'); var transloadit = new TransloaditClient({ authKey : config.transloadit.auth_key, authSecret : config.transloadit.auth_secret }); 

Затем определите параметры для действия, которое вы хотите выполнить. Это может быть либо в виде набора инструкций по сборке:

 var params = { steps: { // ... } }; 

Или, в нашем случае, мы просто предоставляем ID нашего шаблона:

 var params = { template_id: 'YOUR-TEMPLATE-ID' }; 

Чтобы создать подпись:

 var sig = transloadit.calcSignature(params); 

В результате получается хеш, содержащий подпись – своего рода токен доступа, а также параметры, необходимые для вызова службы. Так что наш объект sig будет выглядеть примерно так:

 { signature: "fec703ccbe36b942c90d17f64b71268ed4f5f512", params: { template_id: 'YOUR-TEMPLATE-ID', auth: { key: 'idfj0gfd9igj9dfjgifd8gfdj9gfdgf', expires: '2015-06-25T10:05:35.502Z' } } } 

Чтобы передать это нашим шаблонам Handlebars, чтобы наш JavaScript мог их использовать, нам нужно создать очень простого помощника:

 app.engine('.hbs', exphbs( { extname: '.hbs', defaultLayout: 'default', helpers : { json : function(context) { return JSON.stringify(context); } } } )); 

Давайте теперь соберем это вместе, чтобы определить маршрут account , который будет включать нашу форму загрузки аватара:

 // The account page app.get('/account', ensureAuthenticated, function(req, res){ // Require the TransloadIt client var TransloaditClient = require('transloadit'); // Create an instance of the client var transloadit = new TransloaditClient({ authKey : config.transloadit.auth_key, authSecret : config.transloadit.auth_secret }); // Build the Transloadit parameters... var params = { template_id : config.transloadit.template_id }; // ...and generate the signature var sig = transloadit.calcSignature(params); return res.render('account', { user: req.user, sig : sig }); }); 

Затем в соответствующем шаблоне ( views/account.hbs ) начнем с очень простого HTML:

 <h2>Hello, {{ user.username }}</h2> {{# if user.avatar }} <img src="{{ user.avatar.thumbnail }}" id="avatar"> {{else}} <img src="/avatar.png" id="avatar"> {{/if}} <form method="POST" action="/avatar" id="avatar-form"> <input type="file" name="image" id="avatar-upload"> <input type="hidden" name="username" value="{{user.username}}"> </form> 

Обратите внимание, что мы включаем скрытое поле, которое содержит имя пользователя. Мы отправим это в TransloadIt с нашим запросом, чтобы его можно было использовать в наших шаблонах.

Теперь добавьте JavaScript, начиная с некоторой инициализации переменной с помощью нашего помощника json Handlebars:

 var sig = {{{ json sig }}}; 

Теперь мы свяжем плагин TransloadIt с формой загрузки:

 $(function() { $('#avatar-form').transloadit({ wait: true, params: JSON.parse(sig.params), signature: sig.signature, fields: true, triggerUploadOnFileSelection: true, autoSubmit: false, onSuccess: function(assembly) { $('img#avatar').attr('src', assembly.results.thumbnail[0].url + '?' + (new Date()).getTime() ); var derivatives = { thumbnail : assembly.results.thumbnail[0].url, medium : assembly.results.medium[0].url, large : assembly.results.large[0].url }; $.ajax({ type: 'post', url: '/avatar', data: derivatives, success: function(resp){ console.log(resp); } }) } }); }); 

Это сложнее, чем минимальная интеграция, которую мы рассматривали ранее, поэтому давайте рассмотрим ее по очереди.

Мы извлекаем параметры и сигнатуру из переменной sig , которую мы сгенерировали на сервере и затем закодировали как JSON. Поскольку часть params является вложенной, мы используем JSON.parse() чтобы преобразовать ее обратно в объект, из которого TransloadIt извлечет соответствующие параметры.

В инициализации плагина wait установлено значение true , что означает, что мы ждем, пока оба файла не будут загружены и обработаны.

Использование уведомлений о сборке, о которых вы можете прочитать позже в разделе «Расширенное использование», означает, что вам не обязательно будет ждать обработки файла, и в этом случае вы можете установить wait в false .

fields установлено значение true чтобы сообщить плагину, что мы хотим включить дополнительную информацию при отправке файлов на обработку; в нашем случае это скрытое поле формы с именем username , которое мы заполняем именем username , прошедшего аутентификацию.

triggerUploadOnFileSelection используется для отправки файла в Transloadit, как только пользователь выбрал файл, а не когда форма отправлена. autoSubmit запрещает autoSubmit формы, когда результат возвращается из Transloadit, так как мы собираемся сделать это вручную.

onSuccess вызов onSuccess запускается, когда данные возвращаются из Transloadit, что дает нам хэш данных в assembly .

assembly объект содержит свойство results , которое, в свою очередь, содержит свойства для каждого из наших «шагов». Они содержат массив файловых объектов. Поскольку мы загружаем только один файл, они будут массивами, содержащими один элемент. Каждый файловый объект содержит ряд свойств, включая исходное имя файла, метаинформацию, уникальные идентификаторы из Transloadit и другие фрагменты. Чтобы увидеть весь спектр информации, вы можете выйти из нее в консоли и посмотреть. Однако все, что нас действительно интересует, это свойство url , которое содержит URL сгенерированного изображения на S3.

В качестве альтернативы вы можете использовать свойство ssl_url , идентичное url но через HTTPS.

Мы просто извлекаем три URL-адреса по имени соответствующего производного, затем создаем хэш трех производных и их соответствующих URL-адресов.

Чтобы обеспечить визуальную обратную связь с пользователем, мы также берем URL-адрес миниатюры и изменяем аватар на странице, чтобы показать вновь загруженное изображение.

Наконец, мы используем Ajax для отправки этих данных молча обратно в наше приложение.

Вот маршрут avatar для сбора этих данных:

 // Ajax callback for setting the avatar app.post('/avatar', ensureAuthenticated, function(req, res){ req.user.avatar = req.body req.user.save(function(err) { if(err) { return res.send('error'); } return res.send('ok'); }); }); 

В производстве вы, вероятно, захотите санировать и проверять это.

Как вы можете видеть, мы берем хеш производных изображений и их URL, берем текущего аутентифицированного пользователя из req.user , устанавливаем свойство avatar для предоставленного хэша и затем обновляем модель пользователя.

Это только один из возможных подходов. Для более быстрой обратной связи вы можете использовать onResult вызов onResult плагина для получения эскиза сразу после его создания, а не ждать всех трех производных. Вместо того, чтобы использовать Ajax-вызов из вашего клиентского кода для уведомления вашего сервера, вы можете вместо этого предпочесть использовать функцию уведомлений сборки , которая дает дополнительное преимущество при запуске сборок в фоновом режиме, а не задерживает выполнение на клиенте. Консультируйтесь с документацией плагина для полного диапазона вариантов.

На этом мы завершаем наше основное приложение. Не забывайте, что весь источник – включая механизм аутентификации – на Github закончен .

Расширенное использование

Прежде чем закончить, давайте кратко рассмотрим несколько более сложных аспектов TransloadIt.

Другие варианты на стороне клиента

Вам не нужно использовать предоставленный плагин jQuery. В разделе « Проекты сообщества » документации вы найдете несколько альтернатив, в том числе плагин для Bootstrap , плагин для drag n ‘drop , плагин Angular или поддержку простого старого XHR , а также другие.

XHR, возможно, стоит того, чтобы посмотреть на него более подробно. Это простое решение, которое предлагает большую гибкость, при этом вы должны предоставить свой собственный отзыв – например, какой-то индикатор загрузки. Стоит также отметить, что после загрузки файла он пытается определить, когда сборка была завершена, опрашивая сервер с интервалом в 1000 мс.

Уведомления

Вместо того чтобы заставлять пользователей ждать обработки своих загрузок, вы можете использовать уведомления для проверки связи с приложением, когда файлы будут готовы. При таком подходе пользователю нужно только дождаться завершения загрузки.

Уведомления легко реализуются с точки зрения потребителя; просто notify_url инструкции по сборке, например:

 { auth : { ... }, steps : { ... }, notify_url : "http://example.com/webhooks/incoming/transloadit" } 

Когда ваш URL будет проверен Transloadit, предоставленный JSON будет содержать поле signature которое вы можете использовать для проверки того, что уведомление действительно пришло от них. Просто расшифруйте подпись, используя свой секрет авторизации.

Во время разработки вы можете воспользоваться этим прокси-пакетом для тестирования уведомлений о сборке или использовать службу туннелирования, такую ​​как ngrok .

Резюме

В этой серии из двух частей мы подробно рассмотрели TransloadIt , сервис обработки файлов.

В первой части мы рассмотрели некоторые преимущества и недостатки, а затем рассмотрели ключевые концепции.

В этой части мы запачкали руки и создали простой компонент аватара пользователя, используя jQuery, Node.js и Express.

Вы не ограничены jQuery и действительно можете свободно использовать ванильное решение JavaScript или ваш любимый фреймворк. Вам даже не нужно использовать его из клиентского приложения, а когда дело доходит до серверных технологий, у вас есть широкий выбор вариантов. Хотелось бы надеяться, что теперь у вас есть понимание того, как его можно использовать для обработки изображений.

Используете ли вы TransloadIt в своих проектах? Знаете ли вы о лучшем сервисе? Дай мне знать в комментариях.