1. 模块化
2. 第三方包
npm i -S art-template
npm i -S bootstrap
npm i -S formidable
npm i -S jquery
3. 代码区
app.js
/*
* @Author: Robyn
* @Date: 2017-10-31 19:43:28
* @Last Modified by: Robyn
* @Last Modified time: 2017-10-31 19:43:28
*/
//开启服务器
//核心
var http = require("http");
//自定义
var render = require("./render.js");
var router = require("./router.js");
var server = http.createServer();
server.on("request",function(req,res){
//在res中注册一个render方法
render(res);
//为下面代码的执行铺路:下面需要接收路径,根据不同的路径作出不同的处理,我们使用一个单独的模块来完成这个功能
//这个模块我们取名叫做路由
router(req,res);
});
server.listen(3000,function(){
console.log("running");
});
handler.js
/*
* @Author: Robyn
* @Date: 2017-10-31 19:44:19
* @Last Modified by: Robyn
* @Last Modified time: 2017-10-31 19:44:19
*/
//负责处理具体的事物
var fs = require("fs");
var url = require("url");
//第三方
var formidable = require("formidable");
//自定义模块
var modules = require("./modules.js");
module.exports.getIndex = function(req,res){
//封装读取静态文件的代码
// fs.readFile("./data.json",function(err,herosData){
// if(err) {
// return res.end("失败");
// }
// res.render("index.html",JSON.parse(herosData.toString()),function(err,html){
// if(err) {
// return res.end("失败");
// }
// res.end(html);
// });
// });
modules.getAll(function(err,herosData){
if(err) {
return res.end("失败");
}
//渲染
res.render("index.html",herosData,function(err1,html){
if(err1) {
return res.end("失败");
}
res.end(html);
});
});
}
module.exports.getAdd = function(req,res){
res.render("add.html",null,function(err,data){
if(err) {
return res.end(err);
}
res.end(data);
});
}
module.exports.postAdd = function(req,res){
res.writeHead(200,{
"content-type": "text/html;charset=utf-8"
});
// 将原来通过data和end事件接收到的参数 改为通过第三方包formidable来接收
//创建一个formidable对象
var form = new formidable.IncomingForm();
// //调用parse方法
// //回调函数的三个参数:
// // 参数一:err:错误信息
// // 参数二:fields 字段 从浏览器端提交到服务器的一些属性
// // 参数三:files 文件 从浏览器端上传过来的文件
// // 由于formidable会将图片自动保存到一个临时目录下,而我们需要将图片保存到img下,所以需要设置一个图片的保存路径
// form.uploadDir = "./img";
// // 由于formidable默认不会保存文件的后缀名,所以我们需要将图片留后缀名
// form.keepExtensions = true;
form.parse(req, function(err, fields, files) {
if(err) {
return res.end(retMsg(1,"失败"));
}
//新增对象
modules.add(fields,function(err){
res.end(retMsg());
});
// fs.readFile("./data.json",function(err,herosData){
// var heros = JSON.parse(herosData.toString());
// //根据得到的参数创建一个对象
// var obj = {};
// obj.name = fields.name;
// obj.gender = fields.gender;
// //得到heros中最后一条数据的id
// var id = heros.heros[heros.heros.length - 1].id + 1;
// obj.id = id;
// // obj.img = "/" + files.img.path;
// obj.img = fields.img;
// //将对象重新追倒hero中
// heros.heros.push(obj);
// //重新将对象写入到data.json中
// fs.writeFile("./data.json",JSON.stringify(heros,null," "),function(err){
// if(err) {
// return res.end(retMsg(1,"失败"));
// }
// // res.end("<script>alert('新增成功');window.location='/'</script>");
// //返回的数据是交给浏览器中的异步对象的,而异步对象需要的一个json格式的字符串
// //将retobj对象作为返回给浏览器的数据,有这么几属性:
// // statu: 表示的是当前操作的状态码: 0 成功 1 失败
// // msg: 表示当前操作的 文本信息
// // var retObj = {
// // statu: 0,
// // msg: "操作成功"
// // };
// // res.end(JSON.stringify(retObj));
// res.end(retMsg());
// });
// });
})
}
module.exports.getDel = function(req,res){
//del?id=2
var path = req.url;
var id = url.parse(path,true).query.id;
//得到id
modules.del(id,function(err){
if(err) {
return res.end(retMsg(1,"失败"));
}
res.end(retMsg());
});
}
module.exports.postUpload = function(req,res){
var form = new formidable.IncomingForm();
form.uploadDir = "./img";
form.keepExtensions = true;
form.parse(req, function(err, fields, files) {
if(err) {
return res.end(retMsg(1,"失败"));
}
var imgName = "/" + files.img.path;
res.end(retMsg(0,"成功",imgName));
});
}
module.exports.getStatic = function(req,res) {
var path = "." + req.url;
fs.readFile(path,function(err,data){
if(err) {
return res.end("失败");
}
res.end(data);
});
}
//设置一个统一的返回对象
// statu:返回对象的状态 0成功 1失败
// msg:返回对象信息
// imgName: 返回对象的图片名称
function retMsg(statu,msg,imgName) {
var obj = {};
obj.statu = statu || 0;
obj.msg = msg || "成功";
obj.imgName = imgName || "";
//将对象转为字符串返回
return JSON.stringify(obj);
}
modules.js
/*
* @Author: Robyn
* @Date: 2017-10-31 19:45:16
* @Last Modified by: Robyn
* @Last Modified time: 2017-10-31 19:45:16
*/
var fs = require("fs");
//四种类型的方式:
var path = "./data.json";
//查
//得到data.json中所有的数据
// 回调函数
// 两个参数:err,data
module.exports.getAll = function(callback){
fs.readFile(path,function(err,data){
if(err) {
return callback(err);
}
callback(null,JSON.parse(data.toString()));
});
}
//增
// 两个参数:
// obj:要新增的对象(这个对象中没有id属性)
// 回调函数:
// 只有一个参数:当出现错误时的参数,如果没有错误,则没有参数
module.exports.add = function(obj,callback) {
//1.0 先读取出data.json中的所有的数据
this.getAll(function(err,herosData){
if(err) {
return callback(err);
}
//2.0 得到要新增的对象
//一般新增的对象都是没有id的,所以我们需要得到id,给这个对象赋值
var id = herosData.heros[herosData.heros.length -1].id + 1;
obj.id = id;
//3.0 将新增的对象添加到对象的数组中
herosData.heros.push(obj);
//4.0重新将数据写入到data.json中
fs.writeFile(path,JSON.stringify(herosData,null," "),function(err){
if(err) {
return callback(err);
}
callback(null);
});
});
}
//改
//删
module.exports.del = function(id,callback){
//要得到要删除数据的id
//得到所有的数据
this.getAll(function(err,herosData){
if(err) {
return callback(err);
}
for(var i = 0 ; i < herosData.heros.length; i ++) {
//根据id得到要删除的数据
if(herosData.heros[i].id == id) {
herosData.heros.splice(i,1);
break;
}
}
//重新将内容写入到data.json中
fs.writeFile(path, JSON.stringify(herosData,null," "),function(err1){
if(err1) {
return callback(err1);
}
callback();
});
});
}
render.js
/*
* @Author: Robyn
* @Date: 2017-10-31 19:45:58
* @Last Modified by: Robyn
* @Last Modified time: 2017-10-31 19:45:58
*/
var fs = require("fs");
var template = require("art-template");
//用来给res对象动态添加一个渲染方法
module.exports = function (res) {
//作用:读取views下面的静态文件
//参数一:url要读取的静态文件的名称 index.html add.html
//参数二:obj 要通过模板引擎渲染的数据
//参数三:callback 给将来调用者设置一个回调函数,读取出数据以后怎么操作由调用者来决定
// 现在callback中有两个参数:第一个参数是err,第二个参数是html
// 由于我们将来对读取出来数据无非作这么几种处理:
// 1)直接返回给浏览器,2)得到一个数据,将数据通过模板引擎进行渲染,渲染以后再返回给浏览器
res.render = function (url,obj,callback) {
//将url转换为可以读取内容的路径
var path = "./views/" + url;
fs.readFile(path,function(err,data){
if(err) {
return callback(err);
}
//将数据进行渲染,并且响应到浏览器中
var html = template.compile(data.toString())(obj || {});
callback(null,html);
});
}
}
router.js
/*
* @Author: Robyn
* @Date: 2017-10-31 19:46:29
* @Last Modified by: Robyn
* @Last Modified time: 2017-10-31 19:46:29
*/
var handler = require("./handler.js");
//路由只是用来分发请求,得到请求的路径,并且判断路径,具体做什么事情,这个文件也不管
module.exports = function(req,res){
//先拿到不同的请求
var url = req.url;
var method = req.method;
//判断
if(url == "/" && method == "GET") {
handler.getIndex(req,res);
} else if( url == "/add" && method == "GET") {
handler.getAdd(req,res);
} else if(url =="/upload" && method == "POST") {
handler.postUpload(req,res);
} else if( url == "/add" && method == "POST"){
handler.postAdd(req,res);
} else if( url.indexOf("/del") == 0 && method == "GET"){
handler.getDel(req,res);
} else if (url.indexOf("/node_modules") == 0 || url.indexOf("/img") == 0) {
handler.getStatic(req,res);
}
}
views下index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hero - Admin</title>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
<style>
.hero-list img {
width: 50px;
}
</style>
</head>
<body>
<header>
<div class="page-header container">
<h1>王者荣耀 <small>英雄管理器</small></h1>
</div>
</header>
<div class="container hero-list">
<a class="btn btn-success pull-right" href="/add">添加英雄</a>
<table class="table table-hover">
<thead>
<th>编号</th>
<th>名称</th>
<th>性别</th>
<th>头像</th>
<th>操作</th>
</thead>
<tbody id="tbody">
{{each heros}}
<tr>
<td>{{$value.id}}</td>
<td>{{$value.name}}</td>
<td>{{$value.gender}}</td>
<td>![]({{$value.img}})</td>
<td><a href="#">查看</a> <a href="#">修改</a> <a onclick="del({{$value.id}})" href="javascript:void(0)">删除</a></td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</body>
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
<script>
function del(id) {
if(confirm("您确定删除吗?")) {
$.ajax({
url: "/del",
type: "get",
data: "id=" + id,
dataType: "JSON",
success: function(result){
if(result.statu == 0) {
alert(result.msg);
window.location = "/";
} else {
alert(result.msg);
}
}
});
}
}
</script>
</html>
views下add.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
table {
margin: 0 auto;
width: 500px;
min-height: 300px;
}
</style>
</head>
<body>
<form id="form" action="/add" method="POST" enctype="multipart/form-data">
<table>
<tr>
<td>姓名</td>
<td><input type="text" name="name" id="name"></td>
</tr>
<tr>
<td>性别</td>
<td><input id="man" type="radio" name="gender" value="男"><label for="man">男</label> <input type="radio" name="gender" id="woman" value="女"><label for="woman">女</label></td>
</tr>
<tr>
<td>图片</td>
<td><img alt="" id="preImg"><input type="file" name="img" id="img"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" id="btn" value="新增"></td>
</tr>
</table>
</form>
</body>
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
<script>
//实现预览功能
$("#img").on("change",function(){
//1.0创建一个formData对象
var fd = new FormData();
//2.0提供参数:
fd.append("img",document.getElementById("img").files[0]);
//一旦选择了图片,我们需要将图片上传到服务器
//为了不影响页面的正常运行,需要异步上传
$.ajax({
url: "/upload",
type: "POST",
data: fd,
//告诉jquery不要验证我们fd中的参数的类型
contentType: false,
//告诉jquery不要检查fd中提交的参数
processData: false,
dataType: "JSON",
success: function(result){
if(result.statu == "0") {
//将返回的图片名称设置给img标签
$("#preImg").attr("src",result.imgName);
} else {
alert(result.msg);
window.location = window.location;
}
}
});
});
// 如果使用异步方式来提交新增的数据,jquery中的ajax方法会默认将提交数据的方式设置为
// 与:enctype="application/x-www-form-urlencoded"
$("#btn").on("click",function(e){
//在h5中为了能够更好的提交上传的参数,所以不能够使用form的serialize方法来序列化参数
//可以用formdata来帮助我们上传参数
//1.0创建一个formData对象
var fd = new FormData();
//2.0将要提交到服务器的参数添加到formData对象
//append方法要传入两个参数:键值对象
fd.append("name",$("#name").val());
fd.append("gender",$("input[type=radio]:checked").val());
// //document.getElementById("img").files[0]:得到上传过来的图片文件
// fd.append("img",document.getElementById("img").files[0]);
fd.append("img",$("#preImg").attr("src"));
//阻止默认事件
e.preventDefault();
$.ajax({
url: "add",
type: "POST",
// data: $("#form").serialize(),
//关闭jquery内容的检查功能
//告诉jquery不要验证我们fd中的参数的类型
contentType: false,
//告诉jquery不要检查fd中提交的参数
processData: false,
data: fd,
dataType: "JSON",
success: function(result){
if(result.statu == 0) {
alert(result.msg);
window.location = "/";//跳转到/
} else {
alert(result.msg);
window.location = window.location;//表示刷新当前页面
}
}
});
})
</script>
</html>
add-同步上传.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
table {
margin: 0 auto;
width: 500px;
height: 300px;
}
</style>
</head>
<body>
<form action="/add" method="POST" enctype="multipart/form-data">
<table>
<tr>
<td>姓名</td>
<td><input type="text" name="name"></td>
</tr>
<tr>
<td>性别</td>
<td><input id="man" type="radio" name="gender" value="男"><label for="man">男</label> <input type="radio" name="gender" id="woman" value="女"><label for="woman">女</label></td>
</tr>
<tr>
<td>图片</td>
<td><input type="file" name="img"></td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="新增"></td>
</tr>
</table>
</form>
</body>
</html>
data.json
{
"heros": [
{
"id": 3,
"name": "安奇拉",
"gender": "女",
"img": "/img/3.jpg"
},
{
"id": 4,
"name": "虞姬",
"gender": "女",
"img": "/img/4.jpg"
},
{
"name": "李白",
"gender": "女",
"id": 5,
"img": "/img\\upload_b1ec223563a3a4691e3835e5695bd412.JPG"
}
]
}
node app.js