您的位置:首页 > 数据库 > Mongodb

nodejs初尝试之nodejs+express+mongodb的视频站

2016-08-22 14:05 537 查看
我们学校暑假七月二十多才放假,暑假只有一个月,在重温js和jquery之后准备尝试一下nodejs。

借了本《Node.js实战》的书,第一个案例就是nodejs+express+mongodb进行博客的搭建。我只是稍微对nodejs有一两天的学习,总觉得按部就班的学很没有斗志,所以直接1上手例子,无奈的发现新人看书确实有点困难,安装express还算顺利,但是mongodb就完全摸不着头脑 了,加上书是express3的版本,现在express已经升级到4.X,在磨了两三天之后,我决定还是去慕课网上取经。正好Scott老师有nodejs+express+mongodb的建站课程。花了大概三四天把一期完成了,现在总结一下这次初尝试。

首先得先在电脑上安装nodejs和git,这个之前就已经装过了,就不再说明,有一点注意的是要安装npm,node的包管理工具。

在去慕课网之前我一直用的是dreamwear,确实是个很好的工具,后来看到sublime text,顿时觉得好高大上,就安装了text3,有趣的地方在于它各种安装插件,刚安装那两三天,一直在研究各种插件和快捷键,此处也就不多说了。

切入正题:

使用Express框架

       1.在cmd命令行中全局安装Express:npm install -g express。安装成功可以看到显示的安装路径,一般在C盘的user/用户名/AppData/Roaming下。找到这个目录可以看到很多其他程序的安装,这里我们就进入npm/node_modules,可以看到安装好的express文件夹。

      2.新建一个Express工程:找到你放置工程项目的目录,比如D:\web_nodejs,这个文件夹是我用来专门放置nodejs项目的。命令行切换到此目录,然后输入命令:express -e blog,这个blog就是项目的名称,如果显示express不是内部命令什么的,就要检查一下是否安装成功express了。

                                


 

     3.安装依赖模块:命令行切换到movie项目下(cd movie),输入命令: npm install,这个命令会将此目录下的package.json中所写的模块安装到node_modules目录下。

     4.启动express:在4.X的版本中启动的命令是:node start,原因是在新版本中入口被设置成bin/www,可以在package.json中看到:  

                                  


                             启动成功的话,在浏览器中输入localhost:3000,可以看到Express的首页。

      以上是自动安装的过程。附上app.js:



安装Mongodb

       1.下载Mongodb:直接搜索,到官网下载,有zip和msi两种格式,都可以使用,我下载的是msi格式,安装的时候选择自定义安装,选择一个简洁的路径安装,比如:D:\Mongodb,安装完后可以看到下图,blog和movie是我后面新建的。

                    


      2.新建文件夹作为存储数据的地方,比如这个blog和movie。

      3.启动数据库:在命令行切换到bin目录下,输入命令:mongod -dbpath d:\Mongodb\movie,就可以启动了数据库了。可以在命令行的反馈中的最后一行看到:waiting for connections on port 27017。在浏览器中输入localhost:27017,可以看到这行英文:It looks like you are trying to access MongoDB over HTTP on the native driver port.表示已经连接上了。接下来就可以操作了。

使用Express和mongodb建立视频站

      接下来就开始项目了。直接使用框架容易搞不清楚每一步都干什么。慕课网中全局安装完express后,不用第二步,采用的是自己建立文件夹,然后新建app.js以及其他文件夹等。

     最开始,app.js是很重要的,它导入需要使用的模块,设置路径,加载路由等。

     因为没有上面的npm install,所以依赖模块需要自己安装,在movie目录下运行命令行,用npm install express jade mongoose,安装这三个模块,其中mongoose是用于管理连接mongodb的。对应的语义在上面的app.js中用注释写好了。还应该全局安装bower,主要负责的是关于前端的东西,比如bootstrap就是用bower安装的。但是安装bootstrap过程中cmd命令行不识别,最后只好用git安装了。

    下面主要讲项目内的路由。

     app.get设置的路由,是浏览器访问时进行的路由选择。另外,首先将各个页面实现,下面代码的末尾是在还没有正式数据交互的时候使用伪造数据来实验的。第一个get的是'/'也就是localhost:3000/,显示的是首页,res.render是express中用于渲染视图的,而且可以返回给客户端。在app.js中我们已经设置了视图的路径和所使用的引擎(‘/views/pages',以及本例中使用的jade模板引擎。)。

index首页

    先拿index首页走一遍流程。首先记得要保持数据库启动,连接数据库的关键在于:

                 var mongoose = require('mongoose');//引入数据库模块

                 mongoose.connect('mongodb://localhost/imooc'); //数据库连接,最后一个就是数据库名

app.js:

var express = require('express');
var path = require('path');
var mongoose = require('mongoose');//引入数据库模块
var Movie = require('./models/movie');  //引入模型
var _ = require('underscore');
var bodyParser = require('body-parser');
var port = process.env.PORT || 3000;
var app = express();

mongoose.connect('mongodb://localhost/imooc');   //数据库连接

app.set('views','./views/pages');
app.set('view engine', 'jade');
//app.use(express.bodyParser());//表单数据格式化
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
app.locals.moment = require('moment');

app.listen(port);

console.log('imooc started on port '+ port);

//编写 路由, 路由规则是访问的url
//首页
app.get('/',function(req, res){   //匹配规则,回调方法
Movie.fetch(function(err, movies){
if(err){
console.log(err);
}
res.render('index',{
title: '视频站 首页',
movies: movies
});
});
});
//详细页
app.get('/movie/:id',function(req, res){   //匹配规则,回调方法
var id = req.params.id;

Movie.findById( id, function(err, movie){
res.render('detail',{   //对应的视图文件
title: '视频站 ' + movie.title,
movie: movie
});
});
});

//后台录入页
app.get('/admin/movie',function(req, res){   //匹配规则,回调方法
res.render('admin',{
title: '视频站 后台录入页',
movie: {
title: '',
doctor: '',
country: '',
year: '',
poster: '',
flash: '',
summary: '',
language: ''
}
});
});

//更新
app.get('/admin/update/:id', function(req, res){
var id = req.params.id;
if(id){
Movie.findById(id, function(err, movie){
res.render('admin',{
title: '视频站 后台更新页',
movie: movie
});
});
}
});

//后台录入页post过来的数据
app.post('/admin/movie/new', function(req, res){
var id = req.body.movie._id;
var movieObj = req.body.movie;
var _movie;

if(id !== 'undefined'){
Movie.findById(id , function(err, movie){
if(err){
console.log(err);
}
_movie = _.extend(movie, movieObj);
_movie.save(function(err, movie){
if(err){
console.log(err);
}
res.redirect('/movie/' + movie._id);
});
});
}
else{
_movie = new Movie({
doctor: movieObj.doctor,
title: movieObj.title,
country: movieObj.country,
language: movieObj.language,
year: movieObj.year,
poster: movieObj.poster,
summary: movieObj.summary,
flash: movieObj.flash
});

_movie.save(function(err, movie){
if(err){
console.log(err);
}
res.redirect('/movie/' + movie._id);
});
}
});

app.get('/admin/list',function(req, res){   //匹配规则,回调方法
Movie.fetch(function(err, movies){
if(err){
console.log(err);
}
res.render('list',{
title: '视频站 列表页',
movies: movies
});
});
});

//list页面删除动作
app.delete('/admin/list',function(req, res){
var id = req.query.id;

if(id){
Movie.remove({_id: id},function(err, movie){
if(err){
console.log(err);
}
else{
res.json({success: 1});
}
});
}
});
/*伪造数据
app.get('/',function(req, res){   //匹配规则,回调方法
res.render('index',{
title: '视频站 首页',
movies: [{
title: '一',
_id: 1,
poster:'http://r3.ykimg.com/051600000530EEB63675839160D0B79D5'
},{
title: '二',
_id: 2,
poster:'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1'
},{
title: '三',
_id: 3,
poster:'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1'
},{
title: '四',
_id: 4,
poster:'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1'
},{
title: '五',
_id: 5,
poster:'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1'
},{
title: '六',
_id: 6,
poster:'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1'
}]
});
});

app.get('/movie/:id',function(req, res){   //匹配规则,回调方法
res.render('detail',{   //对应的视图文件
title: '视频站 详情页',
movie: {
doctor: '导演',
country: '国家1',
title: '1',
year: '2007',
poster: 'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1',
language: '英语',
flash: 'http://',
summary: 'pinglun'
}
});
});

app.get('/admin/movie',function(req, res){   //匹配规则,回调方法
res.render('admin',{
title: '视频站 后台录入页',
movie: {
title: '',
doctor: '',
country: '',
year: '',
poster: '',
flash: '',
summary: '',
language: ''
}
});
});

app.get('/admin/list',function(req, res){   //匹配规则,回调方法
res.render('list',{
title: '视频站 列表页',
movies: [{
title: '一',
_id: 1,
doctor: '导演',
country: '国家1',
year: '2007',
poster: 'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1',
language: '英语',
flash: 'http://'
},{
title: '二',
_id: 2,
doctor: '导演',
country: '国家1',
year: '2007',
poster: 'http://www.iqiyi.com/dianying/20100628/n22290.html#vfrm=19-9-0-1',
language: '英语',
flash: 'http://'
}]
});
});
*/


schemas/movie.js

    schemas是mongodb数据库里的模式,用于定义数据结构的(json格式),这里还定义了方法,在用exports导出这个文件后,可以通过MovieSchema调用对应的属性方法。

//模式
var mongoose = require('mongoose');

var MovieSchema = new mongoose.Schema({//存放字段,类型
doctor: String,
title: String,
language: String,
country: String,
summary: String,
flash: String,
poster: String,
year: Number,
meta: {
createAt: {
type: Date,
default: Date.now()
},
updateAt: {
type: Date,
default: Date.now()
}
}
});
//存储数据之前调用此方法
MovieSchema.pre('save', function(next){
if (this.isNew){
this.meta.createAt = this.meta.updateAt = Date.now();
}else{
this.meta.updateAt = Date.now();
}

next();
});

MovieSchema.statics = {
fetch: function(cb){    //查找所有
return this
.find({})    //内置的方法
.sort('meta.updateAt')    //以这个方法排序
.exec(cb);        //传递给回调函数

},
findById: function(id, cb){   //按id查找
return this
.findOne({_id: id})   //内置的方法
.exec(cb);
}
};

module.exports = MovieSchema;


/models/movie.js

    models是mongoose中的模型,与mongodb中的document一一对应。使用 ‘ mongoose.model ’ 将schemas打包生成一个model后,model会生成所有的document的update,add,save,read method。注意的是要记得用require引入mongoose和对应的schema。因此在app.js中我们需要引入models(第四行)。在路由中获得从model(从schemas中传递过来)传递过来的数据。在路由中再把数据传到视图页面进行渲染再呈现出来。

//模型
var mongoose = require('mongoose');
var MovieSchema = require('../schemas/movie');
var Movie = mongoose.model('Movie', MovieSchema);//传入模型名字以及模式

module.exports = Movie;


     以下三幅图都是视图页面,使用的jade引擎,第一次用,就被它的缩进格式弄得要发疯,有的时候明明对了依然给报错,故意把格式改掉以后再改回来也许就好了。等开始博客系统的时候试试ejs,看起来好像要人性化一点。



     最后首页显示效果如下,此时数据库中有两条数据。不过我的海报连接不能导入正确的图片,所以是用alt取代的。



admin录入页

    接下来说后台录入页,公共代码已经展示完毕,这里贴出jade视图代码,这个地方出了不少岔子,一定要注意的是jade的代码格式,而且貌似一定要用tab缩进,我用空格键打老是报错。

    这里注意的是这是个表单页面,采用post提交数据,对应的是/admin/movie/new,可以在app.js中找到这个路由。

     这里有一个表单隐藏域,不会显示出来,但是在数据库中会自动生成一个相应的_id,这里要注意的是app.js中的6行和16行,主要是16行要设置为true,当extended为false的时候,值就为'String'或'Array'形式,为true的时候,则可为任何数据类型。

    重点看app.js的对应路由,首先获取数据库返回的movie对象和id,要对id进行判断,如果不是undefined就从数据库中找到对应的那条数据,返回并显示到这个电影的播放页/movie/+id。如果是的话,就新建一个movie对象并把数据存进去,再跳转到对应播放页。

extends ../layout
block content
.container
.row
form.form-horizontal(method="post",action="/admin/movie/new")
input(type="hidden", name="movie[_id]", value="#{movie._id}")  //-表单隐藏域
.form-group
label.col-sm-2.control-label(for="inputTitle") 电影名字
.col-sm-10
input#inputTitle.form-control(type="text",name="movie[title]",value="#{movie.title}")
.form-group
label.col-sm-2.control-label(for="inputDoctor") 导演
.col-sm-10
input#inputDoctor.form-control(type="text",name="movie[doctor]",value="#{movie.doctor}")
.form-group
label.col-sm-2.control-label(for="inputCountry") 国家
.col-sm-10
input#inputCountry.form-control(type="text",name="movie[country]",value="#{movie.country}")
.form-group
label.col-sm-2.control-label(for="inputLanguage") 语言
.col-sm-10
input#inputLanguage.form-control(type="text",name="movie[language]",value="#{movie.language}")
.form-group
label.col-sm-2.control-label(for="inputPoster") 海报地址
.col-sm-10
input#inputPoster.form-control(type="text",name="movie[poster]",value="#{movie.poster}")
.form-group
label.col-sm-2.control-label(for="inputFlash") 片源地址
.col-sm-10
input#inputFlash.form-control(type="text",name="movie[flash]",value="#{movie.plash}")
.form-group
label.col-sm-2.control-label(for="inputYear") 上映年份
.col-sm-10
input#inputYear.form-control(type="text",name="movie[year]",value="#{movie.year}")
.form-group
label.col-sm-2.control-label(for="inputSummary") 电影简介
.col-sm-10
input#inputSummary.form-control(type="text",name="movie[summary]",value="#{movie.summary}")
.col-sm-ofset-2.col-sm-10
button.btn.btn-primary(type="submit") 提交


剩下的两个页面就不用多说了,贴出jade代码,按照上述的思路过一遍即可。

list.jade

extends ../layout
block content
.container
.row
table.table.table-hover.table-bordered
thead
tr
th 电影名字
th 导演
th 国家
th 上映年份
th 录入时间
th 查看
th 更新
th 删除
tbody
each item in movies
tr(class="item-id-#{item._id}")
td #{item.title}
td #{item.doctor}
td #{item.country}
td #{item.year}
td #{moment(item.meta.updateAt).format('MM/DD/YYYY')}
td: a(target="_blank", href="../movie/#{item._id}") 查看
td: a(target="_blank", href="../admin/update/#{item._id}") 修改
td
button.btn.btn-danger.del(type="button", data-id="#{item._id}") 删除
script(src="/js/admin.js")

detail.jade
extends ../layout
block content
.container
.row
each item in movie
.col-md-7
embed(src="#{movie.flash}",allowFullScreen="true",quality="high",width="720",height="600",align="middle",type="application/x-shockwave-flash")
.col-md-5
dl.dl-horizontal
dt 电影名
dd= movie.title
dt 导演
dd= movie.doctor
dt 国家
dd= movie.country
dt 语言
dd= movie.language
dt 上映年份
dd= movie.year
dt 简介
dd= movie.summary

最后是进行封装规范化,也就是变得更框架,先用bower init完成前端的设定,最后生成了bower.json,



再是后端的设置,使用npm init,最后生成了package.json



这就是全部了。只是我对这近一个礼拜的学习的一个总结,大概了解了流程以后再去学习基础应该会简单很多。也许还有不对的描述,日后学好了再做修改。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息