《Node入门》读书笔记——用Node.js开发一个小应用

时间:2023-02-02 17:56:00

如需转载请注明出处 http://blog.csdn.net/as645788

Android APP的开发告一段落,一个稳定的、实现了基本功能的APP已经交付用户使用了!我和老板交流了下,接下来准备转战Node.js了,而且一部分前端的功能也要做进去!哈哈哈~~~接下来要朝一个全(zuo)栈(si)工程师进发了,想想都有点小激动呢!这几天一直在学新东西,HTML CSS JavaScript jQuery SQL bootstrap Node.js ···········


(好吧,看着这么多前端+后端的知识,我已经凌乱在风中了-。-·····)
要学的东西是蛮多的,时间又紧,后来我在知乎一番搜索后决定主攻Node.js,SQL等用到时再去查语句怎么写,JavaScript学习基本的语法和关键的一些思想(回调、闭包····)HTML CSS 力求能看懂,jQuery也尝试着写,这里先给大家推荐一个不错的网站,世界上最大学习网站开发的地方 w3schools (简单直白的英文网站,可以锻炼下自己看英文文档的能力,貌似需要*,请自备*),没有*的童鞋可以去 菜鸟教程(中国人自己翻译的网站,内容基本一致),小小的吐槽下:w3schools 的exercise太水了,几道题基本做法都差不多,最多就修改两三个地方,并且学完之后的测试居然就是24道选择题(很多还都是判断题······),还以为很难呢·······不过貌似人家盈利的方式就是最后学完后的certificate,价格是惊人的 95 美刀(在国外知识确实很值钱-。-)


废话不多说,进入正题吧,我是在网上看的这本《Node入门》总共就42页,一边看一边背书里的代码自己写,一天就看完了,还是有不少收获的。
首先咱们要安装Node.js,我用的是Windows版本的安装包,直接下载安装就好,然后大家还需要装一下sublime text 3。下面是我们最终要达到的三个目标。

  • 用户可以通过浏览器使用我们的应用。
  • 当用户请求http://127.0.0.1:8888/start时,可以看到一个欢迎页面,页面上有一个文件上传的表单。
  • 用户可以选择一个图片并提交表单,随后文件将被上传到http://127.0.0.1:8888/upload,该页面完成上传后会把图片显示在页面上。

最后的效果是这样:《Node入门》读书笔记——用Node.js开发一个小应用

《Node入门》读书笔记——用Node.js开发一个小应用

书里有一部分关于直接把函数作为参数进行传值的话我觉得讲的很好,现在摘录出来方便大家理解下什么是 函数式编程
行为驱动执行

请允许我再次脱离主题,在这里谈一谈函数式编程。

将函数作为参数传递并不仅仅出于技术上的考量。对软件设计来说,这其实是个哲学问题。想想这样的场景:在index文件中,我们可以将router对象传递进去,服务器随后可以调用这个对象的route函数。

就像这样,我们传递一个东西,然后服务器利用这个东西来完成一些事。嗨那个叫路由的东西,能帮我把这个路由一下吗?

但是服务器其实不需要这样的东西。它只需要把事情做完就行,其实为了把事情做完,你根本不需要东西,你需要的是动作。也就是说,你不需要名词,你需要动词

理解了这个概念里最核心、最基本的思想转换后,我自然而然地理解了函数编程。

关于Node.js还有一点需要理解的是阻塞IO与非阻塞IO(使用回调函数)的不同

简单来说,假设我们要去快餐店吃饭,有两种不同服务模式的快餐店,一种是基于事件驱动的(我们的node服务器),一种不是(像iis,apache)。对于传统的服务器,在接收到你的请求之后,直到他完成你的请求,否则他不会去接待下一个用户。当服务员输入你的订单之后还有很多事情要做,处理你的支付,帮你倒水,还有一段时间(不确定时长)去等待厨房准备好你的汉堡。服务员(相当于服务器上的线程)每次只能接待一位顾客,直到完成当前顾客的接待之后,才会去接待下一位顾客。很显然,这种方式效率不高,他浪费了太多的时间在等待厨房做汉堡的工作上。而现实中的快餐店采用的是另外一种模式,当接收到你的订单之后,他会给你一个号码牌,这个号码牌就相当于回调函数。接着他会去接待下一位顾客。当你的订餐准备好之后,服务员会呼叫你的号码叫你来取餐。这就是node采用的模式,看得出他要高效的多。

一路看着书往下写,我们把这个小Node.js程序分成了三个主要的模块:server(服务器)、router(分发处理不同的请求)、requestHandle(真正处理请求的部分),剩下的就是细节的完善。


最后贴上全部的源码来供大家学习吧,windows下有一个坑,那个坑花了我两个小时才找到,具体见代码·····

主页文件index.js

var server = require('./server'); //本地写的server.js 文件
var router = require('./router');
var requestHandle = require('./requestHandle');

var handle = {};
handle["/"] = requestHandle.start;
handle
['/start'] = requestHandle.start;
handle
['/upload'] = requestHandle.upload;
handle
['/directory'] = requestHandle.directory;
handle
['/display'] = requestHandle.display;


server.start(router.route,handle);

server.js

//调用node.js的自带模块http,并将其返回值赋值给 var http
var http = require("http");
var url = require("url");

function start(route, handle) {
//用onRequest作为回调函数
function onRequest(request, response) {

var pathName = url.parse(request.url).pathname;
var postDate = "";

console.log("Request for " + pathName + " has received.");
route(pathName,handle,response,request);
};

http.createServer(onRequest).listen(8888);
console.log("server has start");
}

exports.start = start;

router.js

function route(pathName,handle,response,request){

console.log("start router with handle and path name is " + pathName);

if (typeof handle[pathName] === "function") {
return handle[pathName](response,request); //Node.js中这个执行的方法太酷了
}else{
console.log("404 not find ");
response.writeHead(404,{"Content-Type":"text/plain"});
response.write("404 not found");
response.end();
}
}

exports.route = route;

requestHandle.js

var exec = require('child_process').exec;
var queryString = require('querystring');
var fs = require('fs');
var formidable = require('formidable');

function start(response) {
console.log("handler 'start' was called");

//仅仅是为了学习才这么把html写这里的,实际中是绝不能这么丑陋地把html直接写在处理程序里的
var body = '<html>' +
'<head>' +
'<meta http-equiv="content-type" content="text/html;charset=UTF-8">' +
'</head>' +
'<body>' +
//定义submit后的跳转的url是/upload,且是一个post请求
'<form action="/upload" method="post" enctype = "multipart/form-data">' +
'<input type="file" name="upload" multiple="multiple" ><br/>' +
'<input type="submit" value="upload file">' +
'</form>' +
'</body>' +
'</html>';
response.writeHead(200, {
"Content-Type": "text/html"
});
response.write(body);
response.end();
}


function upload(response, request) {
console.log("handler 'upload' was called");
var form = new formidable.IncomingForm();

//关键:这里需要改变form的默认的上传路径,且在windows下由于权限的问题,需要手动创建tmp文件夹
form.uploadDir = 'tmp';
form.parse(request, function(error, fields, files) {
console.log(files.upload.path);
// fs.renameSync(files.upload.path, "/tmp/test.png");
//考虑到这个是Sync同步操作,则必须对异常进行捕获,Node.js不推荐同步操作
try{
fs.renameSync(files.upload.path,'tmp/test.png');
}catch(e){
console.log(e);
}
response.writeHead(200, {
"Content-Type": "text/html"
});
response.write('<img src="/display"/>');
response.end();

});
}

function display(response) {
console.log("handler 'display' was called");
fs.readFile("tmp/test.png",function(error, file) {
if (error) {
console.log("sorry");
} else {
response.writeHead(200,{"Content-Type":"image/png"});
// response.write(data);
response.write(file);
response.end();
};
});
}

/*//响应用户输入的display方法
function display(response,postDate) {
console.log("handler 'display' was called");
response.writeHead(200, {
"Content-Type": "text/plain"
});
response.write("You input " + queryString.parse(postDate).text);
response.end();
}*/

exports.start = start;
exports.upload = upload;
exports.directory = directory;
exports.display = display;