(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 - все работало, думаю, с остальными тоже не возникнет проблем.
Код можно посмотреть здесь

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


 Распечатать »
 Правила публикации »
  Обсудить материал в конференции Microsoft »
Обсудить материал в конференции Дизайн, графика, обработка изображений »
Написать редактору 
 Рекомендовать » Дата публикации: 24.07.2012 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft Windows Remote Desktop Services CAL 2016 Sngl OLP 1License NoLevel User CAL
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год.
Microsoft Windows Professional 10, Электронный ключ
Microsoft Visual Studio Professional w/MSDN AllLng License/Software Assurance Pack OLP 1 License No Level Qualified
Microsoft Windows Server CAL 2016 Sngl OLP 1License NoLevel User CAL
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
3D сканер SENSE
CubeX Trio
CubeX
3D ручка Myriwell, голубая
CubeX Duo
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Новости ITShop.ru - ПО, книги, документация, курсы обучения
Новые материалы
Программирование на Microsoft Access
Краткие описания программ и ссылки на них
Delphi - проблемы и решения
Adobe Photoshop: алхимия дизайна
 
Рассылки Maillist.ru
Новости ITShop.ru - ПО, книги, документация, курсы обучения
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
Обсуждения в форумах
Удалённая работа программистом ASP.NET(C#) (81)
Знанию asp.net(C#) . Знаком с паттернами проектирования по книги M.Файлера.Знание HTML,...
 
Пишу программы на заказ профессионально (1911)
Пишу программы на заказ на языках Pascal (численные методы, списки, деревья, прерывания) под...
 
Пишу программы на заказ для студентов (87)
Пишу для студентов на с, с++, паскаль в средах ms visual studio, qt, builder, borland c, delphi....
 
Программы Delphi на заказ (223)
Пишу программы в среде Delphi на заказ http://bddelphi.ucoz.ru/
 
Помощь по MS Access (319)
Доброе время суток. Случайно оказался на этом сайте, искал статьи по OLAP. Вижу, что...
 
 
 



    
rambler's top100 Rambler's Top100