(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Своё приложение на Node.js с хранением в Dropbox - это просто (исходники)

Источник: habrahabr
andbas

Несмотря на то, что главным моим хобби так и остаются роботы, я трачу немало усилий, чтобы оставаться в трендах своей основной стези - программирования. Волей судьбы недавно удалось познакомиться с Node.js, я узнал о его web фреймворке express, подружился с новым для себя template engine Jade и в довершение ко всему связал все это с папкой в Dropbox.
image
В этом посте я постараюсь коротко рассказать, как можно организовать web-сервис для хранения файлов, используя лишь бесплатные решения. 
Всех заинтересованных - прошу под кат.

Подготовим плацдарм


Итак, нам понадобится:
  • Node.js установленный на локальной машине
  • Аккаунт в Dropbox
  • Сервер Node.js приложений (если захочется запустить сервис не только локально)

Если с первыми двумя пунктами все должно быть понятно, то на третьем мне бы хотелось остановиться чуть подробнее. Я уже упоминал, что все должно получиться бесплатно, и не собираюсь отступать от своих слов.
В процессе моего "барахтанья" в Node.js мире, я наткнулся на целый ряд платформ готовых предоставить в наше распоряжение Node.js server бесплатно. Лично я испытывал две из них: Heroku и Nodester. В результате я все же остановился на втором, хотя, честно сказать, это решение ничем не обосновано. 
Для регистрации в Nodester необходимо получить купон. Сделать это можно на их сайте или в командной строке через nodester-cli. Мне купон пришел на следующий день после отправления запроса. Это очень быстро, хотя я не исключаю, что мне просто повезло.

Создадим проект


Локально

С чего-то ведь надо начинать. Для этого создадим в любом удобном для нас месте папку (у меня называется habr-nodebox) и в ней файл package.json:

{
  "name": "habr-nodebox",
  "version": "0.0.1",
  "node": "0.6.17",
  "author": "andbas",
  "dependencies": {
    "express": "2.5.x",
    "jade": "0.26.x",
    "dbox": "0.4.x"
  }
}

Поля name, version, author - просто дают некоторую информацию о проекте и могут быть изменены без каких-либо проблем; node - версия Node.js используемая в проекте; в секции dependencies перечисляются все используемые сторонние модули. Как я уже упоминал, в проекте будет использоваться express и jade. Плагин dbox, как понятно из названия, будет использоваться для работы с Dropbox. Я пробовал и другой плагин под названием dropbox, но он, к сожалению, не позволял авторизовать приложение, так как в нем был реализован старый API Dropbox, в котором использовался /token. На данный момент для аутентификации Dropbox использует стандарт oauth. 
После сохранения этого файла в командной строке вызовем: 

npm install    

Если все было написано правильно, то npm скачает все упомянутые в dependencies модули и установит их в текущую директорию.
Помимо этого, создадим еще две папки public и view. Первая будет использоваться для статических файлов (CSS, JS и других), в то время как view будет использоваться для шаблонов Jade.

Тем временем в Dropbox

Если мы хотим получить возможность складывать какие-то файлы в Dropbox, нам необходимо выполнить несколько действий, первым из которых будет получить ключ и секретную строку для нашего приложения. Для этого зайдем на страницу приложений нашего Dropbox аккаунта через браузер и создадим там новое приложение. В поле Access type устанавливаем значение App folder (приложение будет иметь ограниченный доступ только к собственной папке в Dropbox). 
На странице приложения запишем себе куда-нибудь App key и App Secret. Вот собственно первый шаг уже пройден. 
Для автоматизации последующих шагов в авторизации я предлагаю написать небольшой скрипт на все том же node.js. Скрипт следующий (dbox-init.js в папке нашего приложения):

var dbox  = require("dbox"),
    stdin = process.stdin,
    stdout = process.stdout;

ask('App key', /^\S+$/, function(app_key) {
  ask('App secret', /^\S+$/, function(app_secret) {
    var app = dbox.app({ 'app_key': app_key, 'app_secret': app_secret });
    app.request_token(function(status, request_token){
      if(request_token){
        console.log('Please visit ', request_token.authorize_url, ' to authorize your app.');
        ask('Is this done? (yes)', /^yes$/, function(answer) {

          app.access_token(request_token, function(status, access_token){
            console.log('app_key: ' + app_key);
            console.log('app_secret: ' + app_secret);
            console.log('oauth_token: ' + access_token.oauth_token);
            console.log('oauth_token_secret: ' + access_token.oauth_token_secret);
            console.log('uid: ' + access_token.uid);
            process.exit();
          });
        });
      }
    });
  });
});

function ask(question, format, callback) {
 stdin.resume();
 stdout.write(question + ": ");

 stdin.once('data', function(data) {
   data = data.toString().trim();

   if (format.test(data)) {
     callback(data);
   } else {
     stdout.write("It should match: "+ format +"\n");
     ask(question, format, callback);
   }
 });
}

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

node dbox-init

Скрипт проходит вместе с вами все стадии oauth аутентификации в Dropbox и помогает получить все ключи необходимые нам. Шаги следующие:
  • скрипт запрашивает App key и App Secret (те, которые мы получили ранее) и генерирует на их основании ссылку
  • мы копируем ссылку в браузер и авторизуем приложение на работу с нашей учетной записью dropbox, получаем уведомление, что приложение авторизовано
  • на вопрос скрипта о том, прошли ли мы авторизацию смело пишем "yes"
  • получаем и записываем в укромное место данные необходимые для авторизации, а конкретно: app_key, app_secret, oauth_token, oauth_token_secret и uid

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

В бой: набросаем контроллер


Переходим, на мой взгляд, к самому интересному - написание контроллера. Основные действия такие: просматривать все файлы, добавлять новые, получать уже существующие. Не буду томить и сразу предоставлю код (web.js).

var express = require('express'),
    app = express.createServer(express.logger()),
    fs = require('fs'), 
    dropbox  = require("dbox").app({"app_key": process.env['dbox_app_key'], "app_secret": process.env['dbox_app_secret'] }),
    client = dropbox.createClient({oauth_token_secret: process.env['dbox_oauth_token_secret'], oauth_token: process.env['dbox_oauth_token'], uid: process.env['dbox_uid']});

app.use(express.static(__dirname+'/public'));
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view options', { layout: false });
app.use(express.bodyParser());

app.get('/', function(req, res) {
    client.metadata(".", function(status, reply) {
        res.render('index', {
            content : reply
        });
    });
});

app.get('/:path', function(req, res) {
    var path = req.params.path;
    client.get(path, function(status, reply, metadata){
      res.send(reply);
    }); 
});

app.post('/', function(req, res) {
    var fileMeta = req.files['file-input'];
    if (fileMeta) {
        fs.readFile(fileMeta.path, function(err, data) {
            if (err) throw err;
            
            client.put(fileMeta.name, data, function(status, reply) {
                res.redirect('/');
            });
        });
    } else {
        res.redirect('/');
    }
});

var port = process.env['app_port'] // 5000;
app.listen(port, function() {
    console.log("Listening on " + port);
});

Я думаю немного разъяснений, что же здесь происходит, не помешает.

var express = require('express'),
    app = express.createServer(express.logger()),
    fs = require('fs'), 
    dropbox  = require("dbox").app({"app_key": process.env['dbox_app_key'], "app_secret": process.env['dbox_app_secret'] }),
    client = dropbox.createClient({oauth_token_secret: process.env['dbox_oauth_token_secret'], oauth_token: process.env['dbox_oauth_token'], uid: process.env['dbox_uid']});

Объявление основных кирпичиков нашего приложения:
  • express - как уже упоминалось ранее, web framework
  • app - это собственно само web приложение
  • fs - интерфейс для работы с файловой системой
  • dropbox - фабрика для создания клиента Dropbox
  • client - клиент Dropbox упрощающий работу с API

app.use(express.static(__dirname+'/public'));
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.set('view options', { layout: false });
app.use(express.bodyParser());

Инициализация нашего web приложения. Здесь задаем основные параметры: пути к статическим файлам (public), директорию для шаблонов (views), движок этих самых шаблонов (jade), отключаем основной layout, чтобы немного упростить написание и обойтись одним шаблоном, и, в конце концов, передаем нашему приложению bodyParser, который будет разбирать тело приходящих запросов.
А теперь перейдем к главной магии. Далее будут следовать три основных обработчика нашего сервиса.

app.get('/', function(req, res) {
    client.metadata(".", function(status, reply) {
        res.render('index', {
            content : reply
        });
    });
});

Этот код отвечает за главную страницу. На ней мы будем отображать список файлов в нашей папке Dropbox, поэтому делаем запрос через client и получаем метаинформацию. Именно она содержит информацию обо всех файлах в нашем приложении. Эту информацию мы передаем нашему движку шаблонов, который и займется рендерингом страницы.

app.get('/:path', function(req, res) {
    var path = req.params.path;
    client.get(path, function(status, reply, metadata){
      res.send(reply);
    }); 
});

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

app.post('/', function(req, res) {
    var fileMeta = req.files['file-input'];
    if (fileMeta) {
        fs.readFile(fileMeta.path, function(err, data) {
            if (err) throw err;
            
            client.put(fileMeta.name, data, function(status, reply) {
                res.redirect('/');
            });
        });
    } else {
        res.redirect('/');
    }
});

Практически всю работу за нас сделал express. Файл, отправленный в теле post запроса на сервер, был временно сохранен на файловую систему. Нам была представлена вся необходимая для нас информация в объекте req.files['file-input'], где file-input - это атрибут name элемента input формы в html. Нам остается только взять файл из файловой системы и отправить в Dropbox. После этого мы будем перенаправлены на главную страницу.

var port = process.env['app_port'] // 5000;
app.listen(port, function() {
    console.log("Listening on " + port);
});

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

Jade шаблон - все дело в отступах


Первое знакомство с Jade для меня лично было болезненным. Обычный html как-то ближе.
Однако дискомфорт быстро прошел. Вторая страничка была написана уже без неприязни. В общем, привыкаешь быстро. Рекомендую попробовать.
Подискутировать на тему удобства jade это конечно здорово, но пора и код показать. Для этого создадим в папке views файл index.jade и в нем пишем:

!!! 5
html(lang="en")
  head
    title habr-nodebox 
  body
    each item, i in content.contents
      div
        a(href="#{item.path}") #{item.path} - #{item.size}
    div 
      form#upload-form(action="/", method="POST", enctype="multipart/form-data")
        input#file-input(name="file-input", type="file")
        input#submit(value="Upload file", type="submit")    

Я постарался сделать шаблон наиболее прозрачным и понятным. Конечно, добиться при этом выдающихся стилистических результатов не получится, но чем не пожертвуешь ради понятности.
Мы лишь создали простейшую HTML страничку со списком файлов в нашей папке. Для этого мы прошлись в цикле each по всем записям о файлах. Напомню, метаданные content были заботливо получены для нашего шаблона в контроллере. Каждый элемент списка - это ссылка, ведущая нас к методу контроллера, для запросов вида "/:path ". После списка следует форма для загрузки новых файлов. Она состоит из двух input элементов, один для файла, второй для отправки формы.

Запуск


Вот собственно наше приложение и готово, осталось только его запустить. Возможно кто-то обратил внимание, когда читал, что все ключи от Dropbox были записаны как переменные массива process.env[]. Сделано это для того, чтобы не оставлять их в коде и иметь возможность опубликовать приложение без страха быть скомпрометированным. Для того чтобы передать в массив process.env свои значения, достаточно записать их в виде key=value перед вызовом node. В командной строке должно получиться что-то вида:
$ dbox_app_key=abc1qwe2rty3asd dbox_app_secret=123asd123asd123 dbox_oauth_token_secret=aaabbbccc111222 dbox_oauth_token=123asd123asd123 dbox_uid=12345678 node web 

На таких сервисах как Nodester существует возможность установить process.env, так что это был единственный способ без лишних заморочек, что пришел мне в голову. 
Результат, после добавления нескольких файлов, будет выглядеть следующим образом.


Вот собственно и все, если у кого-то есть желание запустить подобный код online - просто загрузите его на любой Node.js сервер. Я испытывал на Nodester - все работало, думаю, с остальными тоже не возникнет проблем.
Код можно посмотреть здесь

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 24.07.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год.
Microsoft Office 365 Профессиональный Плюс. Подписка на 1 рабочее место на 1 год
Microsoft Windows Professional 10, Электронный ключ
Microsoft Office 365 Бизнес. Подписка на 1 рабочее место на 1 год
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Программирование на Microsoft Access
CASE-технологии
Компьютерные книги. Рецензии и отзывы
Программирование на Visual С++
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100