nodejs构建定时发微博工具,其中几个库的用法

2012-08-15 23:12

nodejs构建定时发微博工具,其中几个库的用法

by admin

at 2012-08-15 15:12:01

original http://item.feedsky.com/~feedsky/helloJavaScript/~8514355/692109216/6618683/1/item.html

nodejs构建定时发微博工具,其中几个库的用法

免责声明:本文并非介绍构建成熟系统,只是实现功能,谨慎用于生产环境。

用到的外部库:

express

介绍:一个web框架,提供构建各种web页面的能力。

地址:http://expressjs.com/

安装:npm install express

mysql

介绍:在nodejs中操作mysql数据库的库。

地址:https://github.com/felixge/node-mysql/

安装:npm install mysql

consolidate

介绍:将主流模板引擎集合以便在express中使用。

地址:https://github.com/visionmedia/consolidate.js

安装:npm install consolidate

weibo

介绍:一个微博库,支持国内各大微博平台,不过这里只用到了新浪。之所以选用这个而没有选中node-weibo-oauth库,因为它不支持发送图片,慎用。

地址:https://github.com/fengmk2/node-weibo

安装:npm install weibo

运行原理

定时发微博,功能其实很简单,一部分是用户的操作界面,可以设定新的计划或者删除某条计划。一部分是后台,定时遍历数据库中的计划,将到时的计划拿出来放到发送队列。然后再定时检查发送队列,将队列内容按一定时间间隔挨个发送。

数据库中有两个表,

一个 wb_info 是用来存储用户的consumer key 和consumer secret的,以用户id为索引。

另一个 queque 用来存储计划队列,以用户id跟第一个表关联。

这样只要定时检查queque表,将信息拿出来跟当前时间做对比,如果需要发送,送到发送队列中。

然后定时检查发送队列,如果有需要发送的计划,则根据其用户id从wb_info中取出用户的consumer key和consumer secret,然后用这二者把计划信息发送到微博,并从queque表删除此条计划。

几个库的使用

1.weibo库

每个库都有很多用法和功能,这里只列本应用用到的功能。

var weibo = require('weibo');
//应用的新浪app信息
var sinaApp = {
    key : '*******',
    secret : '********',
    blogType: 'tsina'
}
//初始化weibo对象。
weibo.init(sinaApp.blogType, sinaApp.key, sinaApp.secret);

对于绑定微博等功能,weibo提供了一个中间件的东西。说实话我不太懂,只是的确是这么用的。

//weibo中间件,这样,新浪微博的绑定地址就是 /login?type=weibo。app是expess的一个实例
app.use(weibo.oauth({
    loginPath: '/login',
    logoutPath: '/logout',
    blogtypeField: 'type'
// callbackPath:"/oauth"
}));

在浏览器中使用/login?type=weibo 访问之后走完接下来的流程,页面会默认调回根url / 用户的绑定信息写在req.session.oauthUser中。

//首页的代码
app.get('/', function(req, res){
    var user = req.session.oauthUser
    if(user){
        //如果认证成功(session里有用户信息)
        console.log("oauth success!")
        //将用户id和用户名存储到cookie,下次无需认证直接可以使用。
        res.cookie("userid", user.id);
        res.cookie("username", user.name);
        //将用户的consumerkey和consumersecret存储到数据库,在后台可以直接用这两个信息发送微博,无需用户参与。
        ********
        //认证成功跳转到oauth页面
        res.redirect('oauth');
    }else{
        res.render('index.html');
    }
});

这时候你可以在浏览器端做一些跟微博api的交互,例如:

//官方的例子
weibo.public_timeline({ user: { blogType: 'tsina' } }, function (err, statuses) {
  if (err) {
    console.error(error);
  } else {
    console.log(statuses);
  }
});

这段代码之所以可以运行是因为库会自动根据session中信息构建发送请求。

如果你的代码不是浏览器触发的,二是服务器自己触发的,这时候根本没有session这个东西。就需要单独使用consumer key 和consumer secret来发送请求。

//send a new weibo
weibo.update({
    user: {
        blogType: 'tsina',
        oauth_token_key:'****', //consumer key
        oauth_token_secret:'*******',//consumer secret
        authtype: 'oauth'
    },
    status:'**********' // the content to send
}, function (err, statuses) {
    if (err) {
        console.error(err);
    } else {
        console.log("update weibo success")
    }
});

express 和 mustache

express的基本用法网上很多,就不多说鸟。

mustache是一个轻逻辑的小清新模板,使用起来比较简单。因为二者对接起来比较复杂,而且网上的文章大多都过时了,对新版的express来说都不太适用。

consolidate库将常用的模板引擎都做了封装。这里使用它来做中间介绍人。使得把mustache引入express变得很简单,只许一句话,如下:

//设置模板引擎为mustache,这里使用了consolidate库
var cons = require('consolidate')
app.engine("html", cons.mustache);
//设置模板路径
app.set('views', __dirname + '/views');

app.set('view engine', 'html');
app.set('view options', {
    layout: false
})

模板文件都放在views里面。渲染页面的操作如下。

//中间页面,提醒用户认证成功
app.get('/oauth', function(req, res){
    res.render('oauth.html',{data:{}});
});

关于mustache的入门,见我的一篇文章:http://www.html-js.com/?p=1357

mysql

这个库的使用方法就很普通了,没有什么特别多的内容。

    var Client = require('mysql').Client
    var client = new Client();
    client.user = sql_username;
    client.password =sql_pwd;
    client.query('USE tp_wb');
    //这种用法mysql库会帮你自动转义字符,防止xss攻击
    client.query(
        'INSERT INTO wb_queue '+
        'SET wb_username= ?,wb_id = ?, pic = ?, send_time = ? ,content=?',
        [req.cookies.username,req.cookies.userid,req.target_path , req.time,req.body.wb_content]);
    client.end();

静态文件服务

参考这篇文章:http://www.infoq.com/cn/news/2011/11/tyq-nodejs-static-file-server

先设置express的上传文件临时文件夹:

//设置文件上传临时文件夹
app.use(express.bodyParser({
    uploadDir:'./uploads'
}));

下面是对接到的images*的请求做处理:

//静态图片服务
app.get("/images/*",function(req,res){
    var mime= {
        "css": "text/css",
        "gif": "image/gif",
        "html": "text/html",
        "ico": "image/x-icon",
        "jpeg": "image/jpeg",
        "jpg": "image/jpeg",
        "js": "text/javascript",
        "json": "application/json",
        "pdf": "application/pdf",
        "png": "image/png",
        "svg": "image/svg+xml",
        "swf": "application/x-shockwave-flash",
        "tiff": "image/tiff",
        "txt": "text/plain",
        "wav": "audio/x-wav",
        "wma": "audio/x-ms-wma",
        "wmv": "video/x-ms-wmv",
        "xml": "text/xml"
    };
    var realPath = "."+url.parse(req.url).pathname;
    var ext = path.extname(realPath);
    ext = ext ? ext.slice(1) : 'unknown';
    var contentType = mime[ext] || "text/plain";
    path.exists(realPath, function (exists) {
        if (!exists) {
            console.log("404 request to"+realPath)
            res.writeHead(404, {
                'Content-Type': contentType
            });
            res.write("This request URL " + realPath + " was not found on this server.");
            res.end();
        } else {
            fs.readFile(realPath, "binary", function (err, file) {
                if (err) {
                    console.log(err)
                    res.writeHead(500, {
                        'Content-Type': contentType
                    });
                    res.end(err);
                } else {
                    res.writeHead(200, {
                        'Content-Type': contentType
                    });
                    res.write(file, "binary");
                    res.end();
                }
            });
        }
    });
})

图片上传

表单提交的信息在express中一般存储在req.body中,但是如果有file表单,则文件的信息则会存储到req.files中。

//list页面post处理
app.post('/list', function(req, res) {
    //获得图片文件的临时路径
    var tmp_path = req.files['wb_pic'].path;
    var type=req.files['wb_pic'].type
    //如果存在图片
    if(req.files['wb_pic'].size>0){
        if(req.files['wb_pic'].size>5000000){
            req.session.list_error="图片大小超过5M,提交失败!"
            req.method = 'get';
            res.redirect('/list');
            return;
        }else if(!(type=="image/png"||type=="image/jpg"||type=="image/jpeg"||type=="image/gif")){
            req.session.list_error="图片格式不是png,jpg,gif,提交失败!"
            req.method = 'get';
            res.redirect('/list');
            return;
        }else{
            req.session.list_error=""
        }
        // 指定文件上传后的目录
        var target_path = './images/'+(new Date().getTime()) +"-"+parseInt(Math.random()*100000000)+"-"+ req.files['wb_pic'].name;
        // 移动文件
        fs.rename(tmp_path, target_path, function(err) {
            if (err) throw err;
            // 删除临时文件夹文件,
            fs.unlink(tmp_path, function() {
                if (err) throw err;
                console.log('File uploaded to: ' + target_path + ' - ' + req.files['wb_pic'].size + ' bytes');
                **********
            });
        });
    }else{
        //没有图片的时候
        ********
    }
});