SlideShare a Scribd company logo
1 of 38
如何使用 nodejs 开发 web2.0 站点
记得社区里有说,拿 nodejs 开发 web 站点,还处于刀耕火种的状态


还有人说 nodejs 不适合开发 web 站点,只适合做服务器中间件


真的是这样子吗?




NO , cnode club
雪球网 淘宝指数
https://github.com/xiaojue/tuer.me
Nginx 代理端口和处理静态文件                 static



                              convert


                               cario
npm-modules     Nodejs
                              sendmail


                               redies



                    Mongodb
登陆注册   让 Nodejs 带你了解好玩的后端世

       界
内容发布

图片服务

多版共存

调试方法

运维技巧
├── app.js
├── backup                      ├── node_modules
│ └── tuer201211251454.tar.gz   │ ├── calendar
├── checkService.js             │ ├── canvas
├── help.sh                     │ ├── connect-redis
├── index.js                    │ ├── eventproxy
├── lib                         │ ├── express
├── model                       │ ├── ga
│ ├── base.js                   │ ├── imagemagick
│ └── init.js                   │ ├── jade
├── public                      │ ├── mongodb
│ ├── bootstrap                 │ ├── nodemailer
│ ├── favicon.ico               │ ├── rss
│ ├── images                    │ └── xml2js
│ ├── jquery
│ ├── JSON-js
│ ├── libs
│ ├── seajs
│ └── style
├── README.md
├── routes
│ └── wap
├── routes.js
├── tuer
├── views
│ └── wap
└── wapRoutes.js
Nginx 的一些概念和配置介绍


Nginx 大家就比较熟悉了,和 apache 类似,通过配置就可以实现各种强大的功能。


在这里简单介绍下如何来写适合 nodejs 的 nginx 配置,处理静态服务的就不赘述了。


# 等同于 apache 里的 vhost
server {
    listen 80;
    server_name xxx.com www.xxx.com
    charset utf-8;
    location / {
         If( $http_user_agent ~* “(MSIE)”){
              rewrite . htttp://fuck.you/
              break;
         }
         proxy_set_header Host $host:80;
         proxy_set_header X-Forwarded-For $remote_addr;
        proxy_pass http://localhost:3000; # 你懂的。
    }
}

代理端口和屏蔽 IE 就是这么简单 ~
Mongodb 的一些概念和方法介绍

Mongodb 数据库简单来说就是以 bjson 的格式储存应用文档集合的一个东东。

它的语法糖是纯 javascript 的,作为前端看起来一点也不陌生。

它的数据结构是 json 格式的,作为前端每天都在和它打交道。

它支持提供方便多服务器负载均衡的配置【当然我不会】还默认就开启了热区缓存。

【热区缓存就是为你的应用自动缓存敏感数据,减少直接读库操作】

mongodb 因为是键对值的,所以文档大小比 mysql 一类表结构数据库要大,但查询速度快。

比较适合小巧的应用和数据关系简单的网站应用。


一些主要方法:

进入 mongo shell 后可以用 help 方法看全部命令。这里简单介绍一些常用方法。


使用 mongodb 的 nodejs 驱动后,实例化后的 Db 对象拥有以下这些方法。

getCollection ,find.findOne,sort,limit,skip,toArray,remove,update,insert,count.

顾名思义就不解释了, mongodb 的语法比较简单,比 sql 好学多了。
Expressjs 框架的几个重要方法和概念介绍


Expressjs 其实只是对 http.createServer 中的 req , res 包装了丰富的方法,并可以方便监听

get , post 常用请求,然后简单的增加了一个 view 层的配置,

而它的 controller 默认只有一个文件,且没有提供 model 层,需要我们自己简单搭建。


首先 expressjs 在每一个 handle 中提供了 3 个参数
App.get('/index',function(req,res,next){
    // 当一个路由被拦截,它可以对方法进行处理,或者跳过,使用 next 方法。

    // 其中的 req 为请求, res 为响应,当 res.render 或者 res.send , end 方法被调用时,

    // 此请求可视为结束,可在结束前设置 res 的 header ,即响应头信息。


    // render 方法可渲染 views 层中的模版,并传入变量。

    // send 方法则是直接输出信息。


    req 中保存了请求头,请求 url 参数信息, post 信息值, app.use 加载的套件方法,

    和一些便利方法,其中最常用的几个方法和属性为。
#touch model/init.js   // 初始化数据库

init.js →

db.dropDatabase(); //mongdb 语法,语法糖也为 js
db.createCollection(“users”);
db.createCollection(“comment”);
db.createCollection(“diary”);
db.createCollection(“tips”);
db.createCollection(“notebooks”);
db.createCollection(“todos”);
db.notebooks.insert({name:” 默认日记” ,owner:-1});
db.users.insert({
    accounts:”admintest@tuer.me”,
    create_at:new Date(),
    pwd:hex_md5(“1234qwer”),
    nick:”admin”,
    avatar:”data:image/png;base64,xxxxx”,
      profile:” 默认介绍”,
      firends:[],
      notebook:0,
      todocount:0,
      pageurl:””
});
app.js →

var express = require(“express”),
    RedisStore = require('connect-redis'),
    app = express.createServer();

function Configuration(app){
    app.set(“views”,__dirname+'/views');
    app.set(“view engine”,”jade”);
    app.set(“view options”,{layout:false});
    app.use(express.bodypress({uploadDir:__dirname+'/public/images/'}));
    app.use(express.cookieParse());
    app.use(express.session({secret:'keyboard cat',store:new RedisStore}));
    app.use(express['static'](__dirname+'/public'));
    app.use(express.favicon(__dirname+'/public/favicon.ico'),{maxAge:2592000000});
    app.use(app.router);
}
app.configure(function(){
    Configuration(app);
});
require('./routes')(app);
app.listen('3000');
登陆和注册的实现
routes.js →

var login = require('./routes/login'),
index = require('./routes/index'),
register = require('./routes/register'),
/*... 等等,引入拆分后的路由 ..*/
error = require('./routes/error');

module.exports = function(app){
   app.redirect(“home”,”/”);
   app.redirect(“login”,”/login”);
     // 以上为注册快捷跳转

     app.get('*',login.cookies); // 检查 cookie


     // 现在以登陆和注册模块来简单说明
     app.get(“/login”,login.index);
     app.post('login',login.sigin);
     app.get(“logout”,login.logout);

     app.get('/register',register.index);
     app.post('/register/invite',register.invite);
     app.get('/register/active/:active',register.active);
     // 当没有匹配的路由时,转到 error 下的 notFound 方法
     app.get('*',error.notFound);
login.js →

var tuerbase = require('./model/base');

exports.index = index;
exports.sigin = sigin;
exports.logout = logout;
exports.cookies = cookies;

var sigin = function(req,res){
      if(req.session.is_login){
             res.redirect('home');
      }else{
             var proxy = new EventProxy(),
                   accounts = req.body.email.trim(),
                   pwd = req.body.pwd.trim(),
                   remember = req.body.remember,
                   render = function(data){
                         var errorMap = {
                             '001':' 帐号不存在 ',

                             '002':' 帐号密码不正确 '
                       };
                       if(errorMap.hasOwnProperty(data)){
                              req.flash('error',errorMap[data]);
                       }else{
                              signsuccess(req,res,data,accounts,pwd,remember,function(res){
                                     Res.redirect('home');
                              });
                       }
                 }
     }
}
login.js →

proxy.assign('findLoginuser',render);

if(accounts && pwd){
         // 如果校验通过则开始查找
}else{
         proxy.trigger('findLoginuser','001');
}


var md5 = crypto.createHash('md5');
md5.update(pwd);
pwd = md5.digest('hex');
tuerBase.findOne({
      accounts:accounts
},'users',function(err,user){
      if(err || !user) proxy.trigger('findLoginuser','001');
      else{
              if(user['pwd'] === pwd) proxy.trigger('findLoginuser',user);
              else proxy.trigger('findLoginuser','002');
      }
});
login.js →

signsuccess = function(req,res,userdata,accounts,pwd,remember,callback){
    var cookie_accounts = base64.encode(accounts),
    maxAge = config.timeout,
    md5 = crypto.createHash('md5');
    md5.update(pwd);
    var cookie_pwd = base64.encode(md5.digest('hex'));
    if(remeber){
         maxAge = 1000 * 60 * 60 * 24 * 7;
         res.cookie('remember',1,{maxAge:maxAge,path:'/',domain:config.cookiepath});
    }
     res.cookie('accounts',cookie_accounts,{/*... 写入 cookie..*/});

     res.cookie('pwd',cookie_pwd,{/*... 写入 cookie..*/});

     req.session.cookie.expires = false;
     req.session.cookie.maxAge = config.timeout;
     req.session.is_login = true;
     req.session.userdata = userdata; // 写入 session 数据

     req.session.userdata.avatar = Avatar.getUrl(userdata._id); 增加头像地址 - 通过 id 转换
     if(callback) callback(res);
};
login.js →

cookies = function(req,res,next){
    var accounts = req.cookies.accounts,
    pwd = req.cookies.pwd,
    remember = req.cookies.remember;
    if(accounts && pwd){
          //decode base64 字符

          // 通过 findOne 方法去查找 accounts ,再察看是否有这个用户

          // 流程同 siginin 方法。如果为不合法,或者密码错误,或者不存在用户。

          // 则视为没登陆, session 设置为未登陆,调用 next 方法进入下个路由

          // 如果密码和用户匹配,则调用 siginsuccess 方法,进行正确的登陆操作
     }
}

register.js

var mail = require('../lib/mail');

exports.invite = invite; // 邀请方法

exports.active = active; // 激活方法
注册的流程为 , 通过用户输入的邮箱和昵称,发送激活信,带上时间戳和初始化随机密码,

用户登陆邮箱后,通过激活链接,激活自己的帐号,超时则失败。


其中的参数都使用 base64 和 md5 加密。


下面看下如何使用 nodejs 发邮件:

./lib/mail.js →

var nodemailer = require('nodemailer'),
util = require('../lib/util.js');

var transport = nodemailer.createTransport('Sendmail','/usr/sbin/sendmail'); // 本地 sendmail 的执行路径

exports.send_mail = function(options, callback) {
  var _conf = {
      sender: 'root@tuer.me',
      headers:{
      }
  };
  util.mix(_conf,options);
  transport.sendMail(_conf, function(err, success) {
      if (err) callback(err);
      else callback(null,success);
  });
}
内容发布的简单实现
./route/diary.js → 看一下简化过的 save 方法和 detail 方法就知道了。

save = function(req,res){
      If(!req.session.is_login){
              res.redirect('login');
              return;
      }
      var content = req.body.content;
      // 这里略过校验
      tuerbase.save({content:content},'diary',function(err,data){
            if(err){
                     req.flash('error',err);
                        res.redirect('back'); // 返回 req 提交的页面
               }else{
                        res.redirect('home'); // 随便指向哪里,写入成功 .
               }
      });
};

// 这里的 handle 为 app.get('/detail/:id',diary.detail);
detail = function(req,res){
       var id = req.params.id;
       If(!id){
               res.redirect('404'); // 也可以设置为 next(); 最后找不到匹配路由则自动到达 404 页面
      }else{
               tuerBase.findDiaryById(id,function(err,diary){
                     if(err || !diary){
                              next();
                     }else{
                             // 这里的 diary/detail 为 views 下的 diary 目录下的 detail 模版
                             res.render('diary/detail',{
                                    content:diary.content
                             });
                        }
               });
FindById 方法如下:

tuerbase.prototype.findById = function(id,collection,callback){
        this.getCollection(collection,function(err,db){
               if(err) callback(err);
               else {
                        var _id;
                        if(typeof id == 'string'){
                               try{
                                        _id = db.db.bson_serializer.ObjectID.createFromHexString(id);
                               }catch(e){
                                        callback(e);
                                        return;
                               }
                        }
                        else _id = id;
                        db.findOne({_id:_id},function(err,data){
                               if(err) callback(err);
                               else callback(null,data);
                        });
               }                                                               Model 层中的 save 方法如下:
        });
}
                                                                               tuerbase.prototype.save = function(data,collection,callback){
                                                                                     this.getCollection(collection,function(err,db){
                                                                                            if(err) callback(err);
  View 写法:                                                                                  else{
                                                                                                     data['created_at'] = new Date();
  如果使用 jade 模版,则 views 应                                                                             db.insert(data,function(err,data){
                                                                                                            if(err) callback(err);
  div #{content}                                                                                            else callback(null,data);
                                                                                                     });
  输出则为 <div>content 内容 </div>                                                               }
                                                                                     });
                                                                               }

 就是这么简单,你绝对可以。
如何处理图片?
./routes/diary.js                                                                    删除临时文件的代码
                                                                                     util.remove_temp = function(path){
带图片上传的 save 方法                                                                                fs.unlink(path,function(){
                                                                                                        if(err) throw err;
                                                                                              });
save = function(req,res){                                                            };
         var uploadPic = req.files.uploadPic,
         temp_path = uploadPic.path,                                                 批量产生缩略图代码
         type = function(){                                                          util.bacthImages = function(path,callback){
                  var _type;                                                                   var proxy = new EventProxy(),
                  try{                                                                         finish = function(){
                            _type = '.'+uploadPic.type.split('/')[1];                                    callback(null);
                  }catch(e){                                                                   };
                            return '.undef';                                                   var queue = [80,150,300,500,'unlink'],
                  }                                                                            picname = Path.basename(path),
                  return _type;                                                                Index = 0;
         }(),                                                                                  proxy.assign(80, 150, 300, 500, 'unlink', finish);
         filename = path.basename(temp_path),                                                  var resizesize = function(i){
         picname = filename + type,                                                                      var ret = {
         target_path = rootdir + '/public/images/'+picname;                                                        srcPath:path,
         if(uploadPic.size){                                                                                       dstPath:rootdir+'/public/images/'+queue[i]+'/'+picname,
                  If(!type.match(/jpg|png|jpeg|gif/gi)){                                                           width:queue[i],
                            // 错误处理,删除 temp 使用 remove_temp 方法                                                      height:queue[i],
                                                                                                                   Timeout:1000 * 30
                            return;                                                                      }
                    }                                                                                    return ret;
                    if(uploadPic.size > 20971520){                                             };
                            // 大于 2MB
                            return;                                                           function resizehandle(err){
                    }                                                                                  if(err){
                    fs.rename(temp_path,target_path,function(err){                                              try{
                            if(err){                                                                                         fs.unlinkSync(path);
                                      // 给页面抛错误                                                                           fs.unlinkSync(...); 。。。 // 删除所有缩图 ;
                                      req.flash('error',err);                                                    }catch(e){
                                      req.redirect('back');                                                               throw e;
                            }else{                                                                               }
                                      util.bacthImages(target_path,function(err){                      }else{
                                                if(err){                                                         proxy.trigger(queue[index]);
                                                                                                                 Index ++;
                                                          // 抛错                                                  if(queue[index] == 'unlink'){
                                                }else{                                                                     fs.unlink(path,function(){
                                                                                                                                     If(!err) proxy.trigger('unlink');
                                                          // 保存下文件名到相应位置                                                   });
                                                          res.redirect('home'); // 正确的下一步页面                      }else{
                                                                                                                           Imagemagick.resize(resizesize(index),resizehandle);
                                                }                                                                }
                                      });                                                            }
                            }                                                                 }
                    });                                                                       Imagemagick.resize(resizesize(index),resizehandle);
         }else{                                                                      };
                    util.remove_temp(temp_path);
         }
}
如何处理头像? base64 字符?
./lib/avatar.js →
// 根据 uid ,返回头像图片,包含截图和全图

// 前端已经用 flash 压缩了总大小
Var Canvas = require('canvas'),
Image = Canvas.Image;

GetAvatar = function(uid,width,height,callback){
     tuerBase.findUser(uid,function(err,user){
             if(err) callback(err);
             else{
                      var avatar = user.avatar, //base64 字符

                            img = new Image, //canvas 对象中的 image
                            img.onload = function(){},
                            img.onerror = function(err){ callback(err)},
                            img.src = avatar;
                }
       });
}

onload = function(){
      var canvas,ctx;
      if(user.coords && width && height){
              Canvas = new Canvas(width,height);
              ctx = canvas.getContext('2d');
                var coords = user.coords.split(','); // 原来格式为坐标宽度逗号隔开

                ctx.drawImage(img,coords[2],coords[3],coords[0],coords[1],0,0,width,height);// 输出截图后的尺寸
       }else{
                canvas = new Canvas(img.width,img.height);
                ctx = canvas.getContext('2d');
                ctx.drawimage(img,0,0,img.width,img.height,0,0,img.width,img.height);// 输出原始尺寸
       }
       canvas.toBuffer(function(err,buf){
             if(err) callback(err);
             else{
                      if(user.lastMod) callback(null,buf,user.lastMod); // 有最后修改日期,给最后修改日期

                      else callback(null,buf,user.created_at); // 否则给创建日期
user.js → 调用方法

app.get('/avatar/:id',user.avatar);

avatar = function(req,res){
     var uid = req.params.id;
     Avatar.getAvatar(uid,48,48,function(err,buf,lastMod){
          if(err) res.redirect('500');
          else{
                 var D = new Date(),
                 year = 1000 * 60 * 60 * 24 * 365,
                 Expires = new Date(D.valueOf() + year).toString();

                if(req.headers['if-modified-since'] && lastMod == req.headers['if-modified-since']){
                          res.writeHead(304,'not modified');
                          res.end();
                          return;
                }
                res.header('Content-Type','image/png');
                res.header('Last-Modified',lastMod);
                res.header('Expires',Expires);
                res.header('Date',D.toString());
                res.header('Cache-Control','max-age='+year);
                res.send(buf);
           }
     });
};

头像部分搞定啦。不怕用户清不掉缓存鸟 ~
Wap 版? HTML5 版?
Nginx 代理来搞定 ~

if ( $http_user_agent ~* "(MSIE)|(MIDP)|(WAP)|(UP.Browser)|(Smartphone)|
(Obigo)|(Mobile)|(AU.Browser)|(wxd.Mms)|(WxdB.Browser)|(CLDC)|(UP.Link)|
(KM.Browser)|(UCWEB)|(SEMC-Browser)|(Mini)|(Symbian)|(Palm)|(Nokia)|
(Panasonic)|(MOT-)|(SonyEricsson)|(NEC-)|(Alcatel)|(Ericsson)|(BENQ)|(BenQ)|
(Amoisonic)|(Amoi-)|(Capitel)|(PHILIPS)|(SAMSUNG)|(Lenovo)|(Mitsu)|(Motorola)|
(SHARP)|(WAPPER)|(LG-)|(LG/)|(EG900)|(CECT)|(Compal)|(kejian)|(Bird)|(BIRD)|
(G900/V1.0)|(Arima)|(CTL)|(TDG)|(Daxian)|(DAXIAN)|(DBTEL)|(Eastcom)|
(EASTCOM)|(PANTECH)|(Dopod)|(Haier)|(HAIER)|(KONKA)|(KEJIAN)|(LENOVO)|
(Soutec)|(SOUTEC)|(SAGEM)|(SEC-)|(SED-)|(EMOL-)|(INNO55)|(ZTE)|(Windows
CE)|(Wget)|(Java)|(curl)|(Opera)" ) {
            rewrite . http://m.tuer.me/
            break;
          }

Nodejs :

app.get('*',function(req,res,next){
        ga.trackPage(req.url);// 谁说 wap 的 ga 就支持 php 和 java 啦?
        if(!req.accepts("html") && req.accepts("application/xhtml+xml")){
            res.charset = 'UTF-8';
            res.header('Content-Type', 'application/xhtml+xml');
          // 给所有页面增加 header 头,如果支持解析 xhtml+xml 格式的则为正经 wap

浏览器,否则不需要加,解析不了 ~
        }
        next();
  });

HTML5 版本的还要我解释么 ~
怎么调试?发现潜在 bug ?
# node –debug index.js
# node-inspector




# forever start index.js
# forever list
# cat ./root/.forever/xxx.log
如何运维?
Forever ? crontab ? service ?甚至 monit ?


那么我们开始吧 ~ 首先,把你的服务做成一个系统服务 ~ chkconfig 上场了 ~


在 /etc/init.d/ 下建立系统服务脚本

#!/bin/bash
#chkconfig:345 99 01
#description:tuer
export PKG_CONFIG_PATH='/usr/local/lib/pkgconfig'
export LD_LIBRARY_PATH='/usr/local/lib':$LD_LIBRARY_PATH
DIR='/home/tuer2.0'
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
NODE_PATH='/home/tuer2.0/node_modules'
NODE=/usr/local/bin/node

test -x $NODE || exit 0
function start_app {
   forever start "$DIR/index.js" -l "$DIR/logs/tuer.log" -o "$DIR/logs/tuer.out.log" -e "$DIR/logs/tuer.err.log"
}
function stop_app {
   forever stop "$DIR/index.js"
}
case $1 in
   start)
      start_app ;;
   stop)
      stop_app ;;
   restart)
      stop_app
      start_app
      ;;
   *)
   echo "usage: clearstonecc {start|stop}" ;;
esac
exit 0
# service tuer start | restart | stop

# ok... 再也不用担心妈妈突然关机啦 ~


# crontab 定时任务 ~

#!/bin/sh
cd '/home/tuer2.0/'
cur_dir=$(pwd)
# 备份数据库
date_now=`date +%Y%m%d%H%M`
backmongodbFile=tuer$date_now.tar.gz
cd $cur_dir/backup/
/usr/bin/mongodump -h 127.0.0.1 --port 10001 -d node-mongo-tuer -o my_mongodb_dump/
rm *.tar.gz
tar czf $backmongodbFile my_mongodb_dump/
rm my_mongodb_dump -rf
# 重启服务
service tuer restart


# crontab -e
# * */12* * * /bin/sh /home/tuer2.0/help.sh
# 每 12 个小时重启一下, coder 们可以去睡大觉啦。。。
HTTP/1.1 200    3.17 secs:    5039 bytes ==> /
HTTP/1.1 200    2.79 secs:    5039 bytes ==> /
HTTP/1.1 200    3.53 secs:    5039 bytes ==> /
HTTP/1.1 200    3.50 secs:    5039 bytes ==> /
HTTP/1.1 200    3.49 secs:    5039 bytes ==> /
HTTP/1.1 200    3.49 secs:    5039 bytes ==> /
HTTP/1.1 200    3.49 secs:    5039 bytes ==> /
HTTP/1.1 200    3.48 secs:    5039 bytes ==> /
HTTP/1.1 200    3.48 secs:    5039 bytes ==> /
HTTP/1.1 200    3.47 secs:    5039 bytes ==> /

Lifting the server siege...  done.
Transactions:                674 hits
Availability:          100.00 %
Elapsed time:                9.27 secs // 花费时间
Data transferred:      3.24 MB
Response time:             2.91 secs
Transaction rate:     72.71 trans/sec // 平均每秒处理

Throughput:            0.35 MB/sec // 每秒传输量

Concurrency:               211.66 // 最高并
Successful transactions:        674
Failed transactions:           0
Longest transaction:         4.47
Shortest transaction:        0.52
总 结
1,nodejs 可以做网站,而且可以做的很好,很敏捷,模块多,体系相对去年

更加健全。


2,nodejs 真心不只能做打包工具,前端通过 nodejs 学习服务器,后端技术,

门槛低,见效快 , 获取更多视野。


3,nodejs 可以应用的地方很多,如内部系统,中小型项目,本地工具,甚至

混搭 shell 。

(噢, bash 的语法实在太难看懂了)


4, 只有知己知彼,才能百战不殆,只有了解了后端知识,才能更好的服务于

前端。
Q&A
Thank you !

More Related Content

What's hot

Script with engine
Script with engineScript with engine
Script with engineWebrebuild
 
面向开发的前端性能优化
面向开发的前端性能优化面向开发的前端性能优化
面向开发的前端性能优化li qiang
 
恶意网页分析实战
恶意网页分析实战恶意网页分析实战
恶意网页分析实战Huang Toby
 
Web Caching Architecture and Design
Web Caching Architecture and DesignWeb Caching Architecture and Design
Web Caching Architecture and DesignHo Kim
 
advanced introduction to codeigniter
advanced introduction to codeigniteradvanced introduction to codeigniter
advanced introduction to codeigniterBo-Yi Wu
 
jQuery底层架构
jQuery底层架构jQuery底层架构
jQuery底层架构fangdeng
 
前端开发之Js
前端开发之Js前端开发之Js
前端开发之Jsfangdeng
 
Mongo db架构之优先方案
Mongo db架构之优先方案Mongo db架构之优先方案
Mongo db架构之优先方案Lucien Li
 
Kissy design
Kissy designKissy design
Kissy designyiming he
 
Kissy简介
Kissy简介Kissy简介
Kissy简介jay li
 
Mybatis学习培训
Mybatis学习培训Mybatis学习培训
Mybatis学习培训flynofry
 
Java SE 7 技術手冊第五章草稿 - 何謂封裝?
Java SE 7 技術手冊第五章草稿 - 何謂封裝?Java SE 7 技術手冊第五章草稿 - 何謂封裝?
Java SE 7 技術手冊第五章草稿 - 何謂封裝?Justin Lin
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析Justin Lin
 
iOS程序设计-数据持久化
iOS程序设计-数据持久化iOS程序设计-数据持久化
iOS程序设计-数据持久化qiyutan
 
中心教员J2 Ee面试题
中心教员J2 Ee面试题中心教员J2 Ee面试题
中心教员J2 Ee面试题yiditushe
 
基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程zhangdaiping
 
Open Street Map安裝指引 (Ubuntu 12.04)
Open Street Map安裝指引 (Ubuntu 12.04)Open Street Map安裝指引 (Ubuntu 12.04)
Open Street Map安裝指引 (Ubuntu 12.04)Marc Huang
 
那 Angular 那 AJAX 那 RESTful
那 Angular 那 AJAX 那 RESTful那 Angular 那 AJAX 那 RESTful
那 Angular 那 AJAX 那 RESTful功豪 魏
 

What's hot (20)

Script with engine
Script with engineScript with engine
Script with engine
 
面向开发的前端性能优化
面向开发的前端性能优化面向开发的前端性能优化
面向开发的前端性能优化
 
恶意网页分析实战
恶意网页分析实战恶意网页分析实战
恶意网页分析实战
 
Node way
Node wayNode way
Node way
 
Web Caching Architecture and Design
Web Caching Architecture and DesignWeb Caching Architecture and Design
Web Caching Architecture and Design
 
advanced introduction to codeigniter
advanced introduction to codeigniteradvanced introduction to codeigniter
advanced introduction to codeigniter
 
jQuery底层架构
jQuery底层架构jQuery底层架构
jQuery底层架构
 
前端开发之Js
前端开发之Js前端开发之Js
前端开发之Js
 
Mongo db架构之优先方案
Mongo db架构之优先方案Mongo db架构之优先方案
Mongo db架构之优先方案
 
Kissy design
Kissy designKissy design
Kissy design
 
Kissy简介
Kissy简介Kissy简介
Kissy简介
 
Mybatis学习培训
Mybatis学习培训Mybatis学习培训
Mybatis学习培训
 
Java SE 7 技術手冊第五章草稿 - 何謂封裝?
Java SE 7 技術手冊第五章草稿 - 何謂封裝?Java SE 7 技術手冊第五章草稿 - 何謂封裝?
Java SE 7 技術手冊第五章草稿 - 何謂封裝?
 
Ooredis
OoredisOoredis
Ooredis
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
 
iOS程序设计-数据持久化
iOS程序设计-数据持久化iOS程序设计-数据持久化
iOS程序设计-数据持久化
 
中心教员J2 Ee面试题
中心教员J2 Ee面试题中心教员J2 Ee面试题
中心教员J2 Ee面试题
 
基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程基于原型的JavaScript面向对象编程
基于原型的JavaScript面向对象编程
 
Open Street Map安裝指引 (Ubuntu 12.04)
Open Street Map安裝指引 (Ubuntu 12.04)Open Street Map安裝指引 (Ubuntu 12.04)
Open Street Map安裝指引 (Ubuntu 12.04)
 
那 Angular 那 AJAX 那 RESTful
那 Angular 那 AJAX 那 RESTful那 Angular 那 AJAX 那 RESTful
那 Angular 那 AJAX 那 RESTful
 

Viewers also liked

旺铺前端设计分享
旺铺前端设计分享旺铺前端设计分享
旺铺前端设计分享fangdeng
 
W3CTech美团react专场-React Native 初探
W3CTech美团react专场-React Native 初探W3CTech美团react专场-React Native 初探
W3CTech美团react专场-React Native 初探美团点评技术团队
 
美团点评技术沙龙06 - 提高移动端兼容性测试效率工具
美团点评技术沙龙06 - 提高移动端兼容性测试效率工具美团点评技术沙龙06 - 提高移动端兼容性测试效率工具
美团点评技术沙龙06 - 提高移动端兼容性测试效率工具美团点评技术团队
 
美团点评技术沙龙05 - 浅谈前端工程化
美团点评技术沙龙05 - 浅谈前端工程化美团点评技术沙龙05 - 浅谈前端工程化
美团点评技术沙龙05 - 浅谈前端工程化美团点评技术团队
 
3 a forum 2016 陆金所前端进化论 微服务之路(public)
3 a forum 2016 陆金所前端进化论 微服务之路(public)3 a forum 2016 陆金所前端进化论 微服务之路(public)
3 a forum 2016 陆金所前端进化论 微服务之路(public)Ufo Qiao
 
美团点评技术沙龙13-酒旅Hybrid架构体系及演进
美团点评技术沙龙13-酒旅Hybrid架构体系及演进美团点评技术沙龙13-酒旅Hybrid架构体系及演进
美团点评技术沙龙13-酒旅Hybrid架构体系及演进美团点评技术团队
 

Viewers also liked (7)

旺铺前端设计分享
旺铺前端设计分享旺铺前端设计分享
旺铺前端设计分享
 
Meteor
MeteorMeteor
Meteor
 
W3CTech美团react专场-React Native 初探
W3CTech美团react专场-React Native 初探W3CTech美团react专场-React Native 初探
W3CTech美团react专场-React Native 初探
 
美团点评技术沙龙06 - 提高移动端兼容性测试效率工具
美团点评技术沙龙06 - 提高移动端兼容性测试效率工具美团点评技术沙龙06 - 提高移动端兼容性测试效率工具
美团点评技术沙龙06 - 提高移动端兼容性测试效率工具
 
美团点评技术沙龙05 - 浅谈前端工程化
美团点评技术沙龙05 - 浅谈前端工程化美团点评技术沙龙05 - 浅谈前端工程化
美团点评技术沙龙05 - 浅谈前端工程化
 
3 a forum 2016 陆金所前端进化论 微服务之路(public)
3 a forum 2016 陆金所前端进化论 微服务之路(public)3 a forum 2016 陆金所前端进化论 微服务之路(public)
3 a forum 2016 陆金所前端进化论 微服务之路(public)
 
美团点评技术沙龙13-酒旅Hybrid架构体系及演进
美团点评技术沙龙13-酒旅Hybrid架构体系及演进美团点评技术沙龙13-酒旅Hybrid架构体系及演进
美团点评技术沙龙13-酒旅Hybrid架构体系及演进
 

Similar to nodejs开发web站点

Node.js开发体验
Node.js开发体验Node.js开发体验
Node.js开发体验QLeelulu
 
Javascript autoload
Javascript autoloadJavascript autoload
Javascript autoloadjay li
 
Node.js在淘宝的应用实践
Node.js在淘宝的应用实践Node.js在淘宝的应用实践
Node.js在淘宝的应用实践taobao.com
 
Browser vs. Node.js Jackson Tian Shanghai
Browser vs. Node.js   Jackson Tian ShanghaiBrowser vs. Node.js   Jackson Tian Shanghai
Browser vs. Node.js Jackson Tian ShanghaiJackson Tian
 
OpenWebSchool - 11 - CodeIgniter
OpenWebSchool - 11 - CodeIgniterOpenWebSchool - 11 - CodeIgniter
OpenWebSchool - 11 - CodeIgniterHung-yu Lin
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型keelii
 
使用NodeJS构建静态资源管理系统
使用NodeJS构建静态资源管理系统使用NodeJS构建静态资源管理系统
使用NodeJS构建静态资源管理系统Frank Xu
 
Asp.net mvc 培训
Asp.net mvc 培训Asp.net mvc 培训
Asp.net mvc 培训lotusprince
 
Parse, cloud code 介紹
Parse, cloud code 介紹Parse, cloud code 介紹
Parse, cloud code 介紹wantingj
 
J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架wtxidian
 
J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架fangdeng
 
D2_node在淘宝的应用实践_pdf版
D2_node在淘宝的应用实践_pdf版D2_node在淘宝的应用实践_pdf版
D2_node在淘宝的应用实践_pdf版Jackson Tian
 
學好 node.js 不可不知的事
學好 node.js 不可不知的事學好 node.js 不可不知的事
學好 node.js 不可不知的事Ben Lue
 
钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会Zhi Zhong
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejsChi-wen Sun
 
EventProxy introduction - JacksonTian
EventProxy introduction - JacksonTianEventProxy introduction - JacksonTian
EventProxy introduction - JacksonTianJackson Tian
 
Event proxy introduction
Event proxy introductionEvent proxy introduction
Event proxy introductionmysqlops
 
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
從 Web Site 到 Web Application,從 Web Services 到 Mobile ServicesKuo-Chun Su
 

Similar to nodejs开发web站点 (20)

Node.js开发体验
Node.js开发体验Node.js开发体验
Node.js开发体验
 
Javascript autoload
Javascript autoloadJavascript autoload
Javascript autoload
 
Node.js在淘宝的应用实践
Node.js在淘宝的应用实践Node.js在淘宝的应用实践
Node.js在淘宝的应用实践
 
Browser vs. Node.js Jackson Tian Shanghai
Browser vs. Node.js   Jackson Tian ShanghaiBrowser vs. Node.js   Jackson Tian Shanghai
Browser vs. Node.js Jackson Tian Shanghai
 
OpenWebSchool - 11 - CodeIgniter
OpenWebSchool - 11 - CodeIgniterOpenWebSchool - 11 - CodeIgniter
OpenWebSchool - 11 - CodeIgniter
 
异步编程与浏览器执行模型
异步编程与浏览器执行模型异步编程与浏览器执行模型
异步编程与浏览器执行模型
 
使用NodeJS构建静态资源管理系统
使用NodeJS构建静态资源管理系统使用NodeJS构建静态资源管理系统
使用NodeJS构建静态资源管理系统
 
Mvc
MvcMvc
Mvc
 
Asp.net mvc 培训
Asp.net mvc 培训Asp.net mvc 培训
Asp.net mvc 培训
 
Parse, cloud code 介紹
Parse, cloud code 介紹Parse, cloud code 介紹
Parse, cloud code 介紹
 
J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架
 
J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架J engine -构建高性能、可监控的前端应用框架
J engine -构建高性能、可监控的前端应用框架
 
D2_node在淘宝的应用实践_pdf版
D2_node在淘宝的应用实践_pdf版D2_node在淘宝的应用实践_pdf版
D2_node在淘宝的应用实践_pdf版
 
CRUD 綜合運用
CRUD 綜合運用CRUD 綜合運用
CRUD 綜合運用
 
學好 node.js 不可不知的事
學好 node.js 不可不知的事學好 node.js 不可不知的事
學好 node.js 不可不知的事
 
钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会钟志 第八期Web标准化交流会
钟志 第八期Web标准化交流会
 
Backbone js and requirejs
Backbone js and requirejsBackbone js and requirejs
Backbone js and requirejs
 
EventProxy introduction - JacksonTian
EventProxy introduction - JacksonTianEventProxy introduction - JacksonTian
EventProxy introduction - JacksonTian
 
Event proxy introduction
Event proxy introductionEvent proxy introduction
Event proxy introduction
 
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
從 Web Site 到 Web Application,從 Web Services 到 Mobile Services
 

More from xiaojueqq12345

More from xiaojueqq12345 (7)

前端模块化开发
前端模块化开发前端模块化开发
前端模块化开发
 
Fds-给前端用的服务器
Fds-给前端用的服务器Fds-给前端用的服务器
Fds-给前端用的服务器
 
Voluminous_Weibo
Voluminous_WeiboVoluminous_Weibo
Voluminous_Weibo
 
Lithe
LitheLithe
Lithe
 
Web program-peformance-optimization
Web program-peformance-optimizationWeb program-peformance-optimization
Web program-peformance-optimization
 
Mxhr
MxhrMxhr
Mxhr
 
淘宝帮派活动项目总结
淘宝帮派活动项目总结淘宝帮派活动项目总结
淘宝帮派活动项目总结
 

nodejs开发web站点

  • 2. 记得社区里有说,拿 nodejs 开发 web 站点,还处于刀耕火种的状态 还有人说 nodejs 不适合开发 web 站点,只适合做服务器中间件 真的是这样子吗? NO , cnode club 雪球网 淘宝指数
  • 4.
  • 5. Nginx 代理端口和处理静态文件 static convert cario npm-modules Nodejs sendmail redies Mongodb
  • 6. 登陆注册 让 Nodejs 带你了解好玩的后端世 界 内容发布 图片服务 多版共存 调试方法 运维技巧
  • 7. ├── app.js ├── backup ├── node_modules │ └── tuer201211251454.tar.gz │ ├── calendar ├── checkService.js │ ├── canvas ├── help.sh │ ├── connect-redis ├── index.js │ ├── eventproxy ├── lib │ ├── express ├── model │ ├── ga │ ├── base.js │ ├── imagemagick │ └── init.js │ ├── jade ├── public │ ├── mongodb │ ├── bootstrap │ ├── nodemailer │ ├── favicon.ico │ ├── rss │ ├── images │ └── xml2js │ ├── jquery │ ├── JSON-js │ ├── libs │ ├── seajs │ └── style ├── README.md ├── routes │ └── wap ├── routes.js ├── tuer ├── views │ └── wap └── wapRoutes.js
  • 8. Nginx 的一些概念和配置介绍 Nginx 大家就比较熟悉了,和 apache 类似,通过配置就可以实现各种强大的功能。 在这里简单介绍下如何来写适合 nodejs 的 nginx 配置,处理静态服务的就不赘述了。 # 等同于 apache 里的 vhost server { listen 80; server_name xxx.com www.xxx.com charset utf-8; location / { If( $http_user_agent ~* “(MSIE)”){ rewrite . htttp://fuck.you/ break; } proxy_set_header Host $host:80; proxy_set_header X-Forwarded-For $remote_addr; proxy_pass http://localhost:3000; # 你懂的。 } } 代理端口和屏蔽 IE 就是这么简单 ~
  • 9. Mongodb 的一些概念和方法介绍 Mongodb 数据库简单来说就是以 bjson 的格式储存应用文档集合的一个东东。 它的语法糖是纯 javascript 的,作为前端看起来一点也不陌生。 它的数据结构是 json 格式的,作为前端每天都在和它打交道。 它支持提供方便多服务器负载均衡的配置【当然我不会】还默认就开启了热区缓存。 【热区缓存就是为你的应用自动缓存敏感数据,减少直接读库操作】 mongodb 因为是键对值的,所以文档大小比 mysql 一类表结构数据库要大,但查询速度快。 比较适合小巧的应用和数据关系简单的网站应用。 一些主要方法: 进入 mongo shell 后可以用 help 方法看全部命令。这里简单介绍一些常用方法。 使用 mongodb 的 nodejs 驱动后,实例化后的 Db 对象拥有以下这些方法。 getCollection ,find.findOne,sort,limit,skip,toArray,remove,update,insert,count. 顾名思义就不解释了, mongodb 的语法比较简单,比 sql 好学多了。
  • 10. Expressjs 框架的几个重要方法和概念介绍 Expressjs 其实只是对 http.createServer 中的 req , res 包装了丰富的方法,并可以方便监听 get , post 常用请求,然后简单的增加了一个 view 层的配置, 而它的 controller 默认只有一个文件,且没有提供 model 层,需要我们自己简单搭建。 首先 expressjs 在每一个 handle 中提供了 3 个参数 App.get('/index',function(req,res,next){ // 当一个路由被拦截,它可以对方法进行处理,或者跳过,使用 next 方法。 // 其中的 req 为请求, res 为响应,当 res.render 或者 res.send , end 方法被调用时, // 此请求可视为结束,可在结束前设置 res 的 header ,即响应头信息。 // render 方法可渲染 views 层中的模版,并传入变量。 // send 方法则是直接输出信息。 req 中保存了请求头,请求 url 参数信息, post 信息值, app.use 加载的套件方法, 和一些便利方法,其中最常用的几个方法和属性为。
  • 11. #touch model/init.js // 初始化数据库 init.js → db.dropDatabase(); //mongdb 语法,语法糖也为 js db.createCollection(“users”); db.createCollection(“comment”); db.createCollection(“diary”); db.createCollection(“tips”); db.createCollection(“notebooks”); db.createCollection(“todos”); db.notebooks.insert({name:” 默认日记” ,owner:-1}); db.users.insert({ accounts:”admintest@tuer.me”, create_at:new Date(), pwd:hex_md5(“1234qwer”), nick:”admin”, avatar:”data:image/png;base64,xxxxx”, profile:” 默认介绍”, firends:[], notebook:0, todocount:0, pageurl:”” });
  • 12. app.js → var express = require(“express”), RedisStore = require('connect-redis'), app = express.createServer(); function Configuration(app){ app.set(“views”,__dirname+'/views'); app.set(“view engine”,”jade”); app.set(“view options”,{layout:false}); app.use(express.bodypress({uploadDir:__dirname+'/public/images/'})); app.use(express.cookieParse()); app.use(express.session({secret:'keyboard cat',store:new RedisStore})); app.use(express['static'](__dirname+'/public')); app.use(express.favicon(__dirname+'/public/favicon.ico'),{maxAge:2592000000}); app.use(app.router); } app.configure(function(){ Configuration(app); }); require('./routes')(app); app.listen('3000');
  • 14. routes.js → var login = require('./routes/login'), index = require('./routes/index'), register = require('./routes/register'), /*... 等等,引入拆分后的路由 ..*/ error = require('./routes/error'); module.exports = function(app){ app.redirect(“home”,”/”); app.redirect(“login”,”/login”); // 以上为注册快捷跳转 app.get('*',login.cookies); // 检查 cookie // 现在以登陆和注册模块来简单说明 app.get(“/login”,login.index); app.post('login',login.sigin); app.get(“logout”,login.logout); app.get('/register',register.index); app.post('/register/invite',register.invite); app.get('/register/active/:active',register.active); // 当没有匹配的路由时,转到 error 下的 notFound 方法 app.get('*',error.notFound);
  • 15. login.js → var tuerbase = require('./model/base'); exports.index = index; exports.sigin = sigin; exports.logout = logout; exports.cookies = cookies; var sigin = function(req,res){ if(req.session.is_login){ res.redirect('home'); }else{ var proxy = new EventProxy(), accounts = req.body.email.trim(), pwd = req.body.pwd.trim(), remember = req.body.remember, render = function(data){ var errorMap = { '001':' 帐号不存在 ', '002':' 帐号密码不正确 ' }; if(errorMap.hasOwnProperty(data)){ req.flash('error',errorMap[data]); }else{ signsuccess(req,res,data,accounts,pwd,remember,function(res){ Res.redirect('home'); }); } } } }
  • 16. login.js → proxy.assign('findLoginuser',render); if(accounts && pwd){ // 如果校验通过则开始查找 }else{ proxy.trigger('findLoginuser','001'); } var md5 = crypto.createHash('md5'); md5.update(pwd); pwd = md5.digest('hex'); tuerBase.findOne({ accounts:accounts },'users',function(err,user){ if(err || !user) proxy.trigger('findLoginuser','001'); else{ if(user['pwd'] === pwd) proxy.trigger('findLoginuser',user); else proxy.trigger('findLoginuser','002'); } });
  • 17. login.js → signsuccess = function(req,res,userdata,accounts,pwd,remember,callback){ var cookie_accounts = base64.encode(accounts), maxAge = config.timeout, md5 = crypto.createHash('md5'); md5.update(pwd); var cookie_pwd = base64.encode(md5.digest('hex')); if(remeber){ maxAge = 1000 * 60 * 60 * 24 * 7; res.cookie('remember',1,{maxAge:maxAge,path:'/',domain:config.cookiepath}); } res.cookie('accounts',cookie_accounts,{/*... 写入 cookie..*/}); res.cookie('pwd',cookie_pwd,{/*... 写入 cookie..*/}); req.session.cookie.expires = false; req.session.cookie.maxAge = config.timeout; req.session.is_login = true; req.session.userdata = userdata; // 写入 session 数据 req.session.userdata.avatar = Avatar.getUrl(userdata._id); 增加头像地址 - 通过 id 转换 if(callback) callback(res); };
  • 18. login.js → cookies = function(req,res,next){ var accounts = req.cookies.accounts, pwd = req.cookies.pwd, remember = req.cookies.remember; if(accounts && pwd){ //decode base64 字符 // 通过 findOne 方法去查找 accounts ,再察看是否有这个用户 // 流程同 siginin 方法。如果为不合法,或者密码错误,或者不存在用户。 // 则视为没登陆, session 设置为未登陆,调用 next 方法进入下个路由 // 如果密码和用户匹配,则调用 siginsuccess 方法,进行正确的登陆操作 } } register.js var mail = require('../lib/mail'); exports.invite = invite; // 邀请方法 exports.active = active; // 激活方法
  • 19. 注册的流程为 , 通过用户输入的邮箱和昵称,发送激活信,带上时间戳和初始化随机密码, 用户登陆邮箱后,通过激活链接,激活自己的帐号,超时则失败。 其中的参数都使用 base64 和 md5 加密。 下面看下如何使用 nodejs 发邮件: ./lib/mail.js → var nodemailer = require('nodemailer'), util = require('../lib/util.js'); var transport = nodemailer.createTransport('Sendmail','/usr/sbin/sendmail'); // 本地 sendmail 的执行路径 exports.send_mail = function(options, callback) { var _conf = { sender: 'root@tuer.me', headers:{ } }; util.mix(_conf,options); transport.sendMail(_conf, function(err, success) { if (err) callback(err); else callback(null,success); }); }
  • 21. ./route/diary.js → 看一下简化过的 save 方法和 detail 方法就知道了。 save = function(req,res){ If(!req.session.is_login){ res.redirect('login'); return; } var content = req.body.content; // 这里略过校验 tuerbase.save({content:content},'diary',function(err,data){ if(err){ req.flash('error',err); res.redirect('back'); // 返回 req 提交的页面 }else{ res.redirect('home'); // 随便指向哪里,写入成功 . } }); }; // 这里的 handle 为 app.get('/detail/:id',diary.detail); detail = function(req,res){ var id = req.params.id; If(!id){ res.redirect('404'); // 也可以设置为 next(); 最后找不到匹配路由则自动到达 404 页面 }else{ tuerBase.findDiaryById(id,function(err,diary){ if(err || !diary){ next(); }else{ // 这里的 diary/detail 为 views 下的 diary 目录下的 detail 模版 res.render('diary/detail',{ content:diary.content }); } });
  • 22. FindById 方法如下: tuerbase.prototype.findById = function(id,collection,callback){ this.getCollection(collection,function(err,db){ if(err) callback(err); else { var _id; if(typeof id == 'string'){ try{ _id = db.db.bson_serializer.ObjectID.createFromHexString(id); }catch(e){ callback(e); return; } } else _id = id; db.findOne({_id:_id},function(err,data){ if(err) callback(err); else callback(null,data); }); } Model 层中的 save 方法如下: }); } tuerbase.prototype.save = function(data,collection,callback){ this.getCollection(collection,function(err,db){ if(err) callback(err); View 写法: else{ data['created_at'] = new Date(); 如果使用 jade 模版,则 views 应 db.insert(data,function(err,data){ if(err) callback(err); div #{content} else callback(null,data); }); 输出则为 <div>content 内容 </div> } }); } 就是这么简单,你绝对可以。
  • 24. ./routes/diary.js 删除临时文件的代码 util.remove_temp = function(path){ 带图片上传的 save 方法 fs.unlink(path,function(){ if(err) throw err; }); save = function(req,res){ }; var uploadPic = req.files.uploadPic, temp_path = uploadPic.path, 批量产生缩略图代码 type = function(){ util.bacthImages = function(path,callback){ var _type; var proxy = new EventProxy(), try{ finish = function(){ _type = '.'+uploadPic.type.split('/')[1]; callback(null); }catch(e){ }; return '.undef'; var queue = [80,150,300,500,'unlink'], } picname = Path.basename(path), return _type; Index = 0; }(), proxy.assign(80, 150, 300, 500, 'unlink', finish); filename = path.basename(temp_path), var resizesize = function(i){ picname = filename + type, var ret = { target_path = rootdir + '/public/images/'+picname; srcPath:path, if(uploadPic.size){ dstPath:rootdir+'/public/images/'+queue[i]+'/'+picname, If(!type.match(/jpg|png|jpeg|gif/gi)){ width:queue[i], // 错误处理,删除 temp 使用 remove_temp 方法 height:queue[i], Timeout:1000 * 30 return; } } return ret; if(uploadPic.size > 20971520){ }; // 大于 2MB return; function resizehandle(err){ } if(err){ fs.rename(temp_path,target_path,function(err){ try{ if(err){ fs.unlinkSync(path); // 给页面抛错误 fs.unlinkSync(...); 。。。 // 删除所有缩图 ; req.flash('error',err); }catch(e){ req.redirect('back'); throw e; }else{ } util.bacthImages(target_path,function(err){ }else{ if(err){ proxy.trigger(queue[index]); Index ++; // 抛错 if(queue[index] == 'unlink'){ }else{ fs.unlink(path,function(){ If(!err) proxy.trigger('unlink'); // 保存下文件名到相应位置 }); res.redirect('home'); // 正确的下一步页面 }else{ Imagemagick.resize(resizesize(index),resizehandle); } } }); } } } }); Imagemagick.resize(resizesize(index),resizehandle); }else{ }; util.remove_temp(temp_path); } }
  • 26. ./lib/avatar.js → // 根据 uid ,返回头像图片,包含截图和全图 // 前端已经用 flash 压缩了总大小 Var Canvas = require('canvas'), Image = Canvas.Image; GetAvatar = function(uid,width,height,callback){ tuerBase.findUser(uid,function(err,user){ if(err) callback(err); else{ var avatar = user.avatar, //base64 字符 img = new Image, //canvas 对象中的 image img.onload = function(){}, img.onerror = function(err){ callback(err)}, img.src = avatar; } }); } onload = function(){ var canvas,ctx; if(user.coords && width && height){ Canvas = new Canvas(width,height); ctx = canvas.getContext('2d'); var coords = user.coords.split(','); // 原来格式为坐标宽度逗号隔开 ctx.drawImage(img,coords[2],coords[3],coords[0],coords[1],0,0,width,height);// 输出截图后的尺寸 }else{ canvas = new Canvas(img.width,img.height); ctx = canvas.getContext('2d'); ctx.drawimage(img,0,0,img.width,img.height,0,0,img.width,img.height);// 输出原始尺寸 } canvas.toBuffer(function(err,buf){ if(err) callback(err); else{ if(user.lastMod) callback(null,buf,user.lastMod); // 有最后修改日期,给最后修改日期 else callback(null,buf,user.created_at); // 否则给创建日期
  • 27. user.js → 调用方法 app.get('/avatar/:id',user.avatar); avatar = function(req,res){ var uid = req.params.id; Avatar.getAvatar(uid,48,48,function(err,buf,lastMod){ if(err) res.redirect('500'); else{ var D = new Date(), year = 1000 * 60 * 60 * 24 * 365, Expires = new Date(D.valueOf() + year).toString(); if(req.headers['if-modified-since'] && lastMod == req.headers['if-modified-since']){ res.writeHead(304,'not modified'); res.end(); return; } res.header('Content-Type','image/png'); res.header('Last-Modified',lastMod); res.header('Expires',Expires); res.header('Date',D.toString()); res.header('Cache-Control','max-age='+year); res.send(buf); } }); }; 头像部分搞定啦。不怕用户清不掉缓存鸟 ~
  • 29. Nginx 代理来搞定 ~ if ( $http_user_agent ~* "(MSIE)|(MIDP)|(WAP)|(UP.Browser)|(Smartphone)| (Obigo)|(Mobile)|(AU.Browser)|(wxd.Mms)|(WxdB.Browser)|(CLDC)|(UP.Link)| (KM.Browser)|(UCWEB)|(SEMC-Browser)|(Mini)|(Symbian)|(Palm)|(Nokia)| (Panasonic)|(MOT-)|(SonyEricsson)|(NEC-)|(Alcatel)|(Ericsson)|(BENQ)|(BenQ)| (Amoisonic)|(Amoi-)|(Capitel)|(PHILIPS)|(SAMSUNG)|(Lenovo)|(Mitsu)|(Motorola)| (SHARP)|(WAPPER)|(LG-)|(LG/)|(EG900)|(CECT)|(Compal)|(kejian)|(Bird)|(BIRD)| (G900/V1.0)|(Arima)|(CTL)|(TDG)|(Daxian)|(DAXIAN)|(DBTEL)|(Eastcom)| (EASTCOM)|(PANTECH)|(Dopod)|(Haier)|(HAIER)|(KONKA)|(KEJIAN)|(LENOVO)| (Soutec)|(SOUTEC)|(SAGEM)|(SEC-)|(SED-)|(EMOL-)|(INNO55)|(ZTE)|(Windows CE)|(Wget)|(Java)|(curl)|(Opera)" ) { rewrite . http://m.tuer.me/ break; } Nodejs : app.get('*',function(req,res,next){ ga.trackPage(req.url);// 谁说 wap 的 ga 就支持 php 和 java 啦? if(!req.accepts("html") && req.accepts("application/xhtml+xml")){ res.charset = 'UTF-8'; res.header('Content-Type', 'application/xhtml+xml'); // 给所有页面增加 header 头,如果支持解析 xhtml+xml 格式的则为正经 wap 浏览器,否则不需要加,解析不了 ~ } next(); }); HTML5 版本的还要我解释么 ~
  • 31. # node –debug index.js # node-inspector # forever start index.js # forever list # cat ./root/.forever/xxx.log
  • 33. Forever ? crontab ? service ?甚至 monit ? 那么我们开始吧 ~ 首先,把你的服务做成一个系统服务 ~ chkconfig 上场了 ~ 在 /etc/init.d/ 下建立系统服务脚本 #!/bin/bash #chkconfig:345 99 01 #description:tuer export PKG_CONFIG_PATH='/usr/local/lib/pkgconfig' export LD_LIBRARY_PATH='/usr/local/lib':$LD_LIBRARY_PATH DIR='/home/tuer2.0' PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin NODE_PATH='/home/tuer2.0/node_modules' NODE=/usr/local/bin/node test -x $NODE || exit 0 function start_app { forever start "$DIR/index.js" -l "$DIR/logs/tuer.log" -o "$DIR/logs/tuer.out.log" -e "$DIR/logs/tuer.err.log" } function stop_app { forever stop "$DIR/index.js" } case $1 in start) start_app ;; stop) stop_app ;; restart) stop_app start_app ;; *) echo "usage: clearstonecc {start|stop}" ;; esac exit 0
  • 34. # service tuer start | restart | stop # ok... 再也不用担心妈妈突然关机啦 ~ # crontab 定时任务 ~ #!/bin/sh cd '/home/tuer2.0/' cur_dir=$(pwd) # 备份数据库 date_now=`date +%Y%m%d%H%M` backmongodbFile=tuer$date_now.tar.gz cd $cur_dir/backup/ /usr/bin/mongodump -h 127.0.0.1 --port 10001 -d node-mongo-tuer -o my_mongodb_dump/ rm *.tar.gz tar czf $backmongodbFile my_mongodb_dump/ rm my_mongodb_dump -rf # 重启服务 service tuer restart # crontab -e # * */12* * * /bin/sh /home/tuer2.0/help.sh # 每 12 个小时重启一下, coder 们可以去睡大觉啦。。。
  • 35. HTTP/1.1 200 3.17 secs: 5039 bytes ==> / HTTP/1.1 200 2.79 secs: 5039 bytes ==> / HTTP/1.1 200 3.53 secs: 5039 bytes ==> / HTTP/1.1 200 3.50 secs: 5039 bytes ==> / HTTP/1.1 200 3.49 secs: 5039 bytes ==> / HTTP/1.1 200 3.49 secs: 5039 bytes ==> / HTTP/1.1 200 3.49 secs: 5039 bytes ==> / HTTP/1.1 200 3.48 secs: 5039 bytes ==> / HTTP/1.1 200 3.48 secs: 5039 bytes ==> / HTTP/1.1 200 3.47 secs: 5039 bytes ==> / Lifting the server siege... done. Transactions: 674 hits Availability: 100.00 % Elapsed time: 9.27 secs // 花费时间 Data transferred: 3.24 MB Response time: 2.91 secs Transaction rate: 72.71 trans/sec // 平均每秒处理 Throughput: 0.35 MB/sec // 每秒传输量 Concurrency: 211.66 // 最高并 Successful transactions: 674 Failed transactions: 0 Longest transaction: 4.47 Shortest transaction: 0.52
  • 36. 总 结 1,nodejs 可以做网站,而且可以做的很好,很敏捷,模块多,体系相对去年 更加健全。 2,nodejs 真心不只能做打包工具,前端通过 nodejs 学习服务器,后端技术, 门槛低,见效快 , 获取更多视野。 3,nodejs 可以应用的地方很多,如内部系统,中小型项目,本地工具,甚至 混搭 shell 。 (噢, bash 的语法实在太难看懂了) 4, 只有知己知彼,才能百战不殆,只有了解了后端知识,才能更好的服务于 前端。
  • 37. Q&A