你的位置:首页 > Java教程

[Java教程]node.js的一个小例子

安装配置的环节这里就不说了。

这是我学习nodejs以来做的第一个小例子,很简单,就是在第一个页面里输入自己的名字,在第二个页面(有图片)中显示。思路和很多地方都参考 这篇文章,感谢作者。

这篇文章中说得不对的地方希望大家指正。

客户端的步骤是进入127.0.0.1:8000/login进入login.html,填写输入框后进入mian.html。

服务器端的步骤是:

1.通过浏览器传来的url获取路由,判断进入login.html,

2.用户提交表单,在表单action方法中进入main.js,此时使用post方法,携带参数,并根据html中的{name}占位,利用node动态替换。

3.在遇到图片src时,处理图片请求。

 

先贴上两个html文件:

<!DOCTYPE html><html> <head>  <meta charset="utf-8">  <meta name="author" content="Hx2">  <meta name="format-detection" content="telephone=no">  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">  <meta name="apple-mobile-web-app-capable" content="yes">  <meta name="apple-mobile-web-app-status-bar-style" content="black">  <meta name="Description" content="网页描述"/>  <meta name="Keywords" content="关键字"/>  <title></title>  <style>   form {    text-align:center;   }  </style> </head> <body>  <form action="./deliver" method="post">   账户:<input type="text" name="name"/><br/>   密码:<input type="password" name="password"/><br/>   <input type="submit" value="提交"/>  </form>  </body></html>
login.html
<!DOCTYPE html><html> <head>  <meta charset="utf-8">  <meta name="author" content="Hx2">  <meta name="format-detection" content="telephone=no">  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">  <meta name="apple-mobile-web-app-capable" content="yes">  <meta name="apple-mobile-web-app-status-bar-style" content="black">  <meta name="Description" content="网页描述"/>  <meta name="Keywords" content="关键字"/>  <title></title>  <style>   body {    text-align:center;   }   span {    color: brown;   }   img{    width: 100%;   }  </style> </head> <body>  <div>   hello!<span>{name}</span>  </div>  <img src="./showImg" /> </body></html>
main.js

 

首先是主文件,创建一个服务:

1 var http = require('http');2 http.createServer(function(request,response)  {  3   response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});  4   if(request.url!=="/favicon.ico"){5    6    response.end(''); 7   }8 }).listen(8000);  9 console.log('Server running at >View Code

这段中引入node的自带对象http。

response.writeHead是协议头。

然后用if清除对favicon.ico的访问,否则刷新一次就会有两次访问,就避免了未知的错误。

response.end结束请求。

listen是监听某个端口。

 

当一个请求来到服务器的时候,他会获取到url的路径来判断接下来的操作。所以需要添加url模块,利用这个模块来获得url中的相关路径并处理。就是这句:var url = require('url');

连接到router.js文件后,此时主文件中获取路由,通过正则处理掉多余的 “ / ” 以正确匹配router.js里面的方法。同时,用try catch来处理这里可能发生的异常。

完整的main.js文件如下:

var http = require('http');var url = require('url');var router = require('./router.js');http.createServer(function(request,response)  {    if(request.url!=="/favicon.ico"){   pathname = url.parse(request.url).pathname;   pathname = pathname.replace(/\//,'');   try{//如果这一部分有错误,执行下面catch    router[pathname](request,response)   }catch(err){    console.info('router错误' + err);    response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'}); //协议头    response.write(err.toString());    response.end('');    }  }}).listen(8000);  console.log('Server running at id="code_img_closed_0e2fe7c7-ebf4-4b0e-b6f9-240a1f7c1e52" src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif" alt="">
module.exports = { login : function(req,res){   }, deliver : function(req,res){   }, showImg : function(req,res){   }}
View Code

 

在router.js里面有三个操作,他们有个共同点,就是都会读取服务器的本地文件将它写在客户端。这样,把他的操作都提取出来放在一个文件中,有利于管理和调用,这里建个optfile.js存放操作。暂定optfile.js里面有三个方法,分别是同步读取:readfileSync、异步读取:readfile、读取图片:readImg。具体操作等会说。

在router.js里的login中,如果是同步的话可以这样写,他会一步一步向下执行:

login : function(req,res){  res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});  optfile.readfileSync('./view/login.html',res);  res.write(data);  res.end('');},

但是node.js的优势是异步,我们需要做的是异步的。

异步读取文件的方法(在下面会说)需要一个闭包来存储此时的request和response,来保证readFile在异步执行的时候,这里的request和response不会在垃圾回收机制下被自动清除。并且在闭包中加入end()就不存在当异步还没有执行完,主程序先执行完了报错这种情况。

在后面的readFile方法里只需要调用这个闭包就可以了。

这个闭包就是下面的recall(),我们在闭包中对读取的文件进行相关的操作。

login : function(req,res){   response.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});  function recall(data){    res.write(data);    res.end('');  };  optfile.readfile('./view/login.html',recall)}  

在操作比较多的项目中,每一个相同操作(这里是显示到客户端)都要写一个recall函数非常麻烦,所以封装成getRecall方法:

function getRecall(req,res){ res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'}); function recall(data){  res.write(data);  res.end(''); } return recall;}

这样,login就变得很简洁:

login : function(req,res){ recall = getRecall(req,res);//这个recall并不是getRecall,而是getRecall返回的闭包recall optfile.readfile('./view/login.html',recall)},

在router.js里的deliver中,需要使用post或get方法传参,这里使用post方法:

var post = '';req.on('data',function(chunk){ post += chunk;})req.on('end',function(){ post = querystring.parse(post); console.log('收到参数:' + post['name值'] + '\n');}

这时的思路是把收到的参数显示在main.html中,在main.js中接收参数(如果是多个就使用数组),而deliver方法就是带着参数跳转到main.js中。所以想到接下来的步骤应该是:

recall = getRecall(req,res);

optfile.readfile('./view/main.html',recall);

但是上面的post方法相对于上面的两句是异步的,异步传参,所以为了确保这两句在post方法后执行,可以把他放在end事件中。

在recall函数里,他要做的就不仅仅是显示在页面那么简单,而是要寻找到带{}标记的元素,把他替换为传来的参数,重写recall:

function recall(data){ dataStr = data.toString();//字符串 re = new RegExp('{name}','g');//new RegExp和/.../的区别是他可以在里面直接用字符串拼接的形式表示 dataStr = dataStr.replace(re,post['name值']); res.write(dataStr); res.end();}optfile.readfile('./view/main.html',recall);

如果需要传输多个参数,使用数组

arr = ['email','pwd'];function recall(data){ dataStr = data.toString(); for(var i = 0; i < arr.length; i++){  re = new RegExp('{' + arr[i] + '}','g');  dataStr = dataStr.replace(re,post[arr[i]]); } res.write(dataStr); res.end();}

第三个showImg,协议头改成请求图片的协议头,然后读取图片即可:

showImg : function(req,res){ res.writeHead(200,{'Content-Type':'image/jpeg'}); optfile.readImg('./src/xin2.jpg',res);}

完整的router.js:

var optfile = require('./models/optfile.js');
var url = require('url');
var querystring = require('querystring');//post需导入

function getRecall(req,res){
    res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});
    function recall(data){
        res.write(data);
        res.end('');
    }
    return recall;
}

module.exports = {
    login : function(req,res){
        recall = getRecall(req,res);//这个recall并不是getRecall,而是getRecall返回的闭包recall
        optfile.readfile('./view/login.html',recall)
    },
    deliver : function(req,res){
        var post = '';
        req.on('data',function(chunk){
            post += chunk;
        })
        req.on('end',function(){
            post = querystring.parse(post);
            console.log('收到参数:' + post['name'] + '\n');
            console.log('收到参数:' + post['password'] + '\n');
            
            function recall(data){
                dataStr = data.toString();
                re = new RegExp('{name}','g');
                dataStr = dataStr.replace(re,post['name']);
                res.write(dataStr);
                res.end();
            }
            optfile.readfile('./view/main.html',recall);
        })
    },
    showImg : function(req,res){
        res.writeHead(200,{'Content-Type':'image/jpeg'});
        optfile.readImg('./src/xin2.jpg',res);
    }
}

 

来说optfile.js了!optfile.js暂时有三个操作,同、异步读文件和读取图片。

var fs = require('fs');module.exports = { readfileSync : function(path){  var data = fs.readFileSync(path,'utf-8'); }, readfile : function(path,res){  fs.readFile(path,function(err,data){   if(err){    console.log('读文件异常!');   }else{    console.log('读好了');   }  }) }, readImg : function(path,res){  ... }}

大概这么写,但是上面有说,如果想在readFile中添加一个操作,比如说需求中将data显示在客户端,此时可能的思路是把console.log('读好了');替换为res.write(data) 之类。但是不对。因为readFile方法是异步的,在异步里,主线程执行过程中会有很多分线程来执行各种不同的操作,但分线程并不影响主线程的执行。在读取文件的这个分线程中,如果直接执行res.write(data)会报错,因为主线程此时可能已经执行完了。

所以这里要调用到之前写的闭包。虽然recall()在这个线程中执行,但原来的response并没有被清除掉,还在等待着recall();当recall返回一个data时,原来的response接收到了,进行操作。这个闭包储存了原来的req和res用来操作,并解决了分线程end的问题。

readfile : function(path,recall){ fs.readFile(path,function(err,data){  if(err){   console.log('读取文件分线程错误:'+err);   recall('文件不存在');  }else{   console.log('异步读取文件data为:' + data.toString());   recall(data);  } }) console.info('异步读取文件方法执行了');},

如果readFile这里异步线程出错,主线程可以执行,异步可以读取出来,但如果这里有错误并且没有回调recall('文件不存在');的话,记载图标会一直转,所以这里处理异常要使用recall('文件不存在'),其中包括错误提示和结束线程。

readImg和文件读取差不多,就是图片需要用二进制流'binary'的方式读取。

完整的optfile.js:

var fs = require('fs');module.exports = { readfileSync : function(path){  var data = fs.readFileSync(path,'utf-8');  console.info('同步读取文件执行完毕,data为:' + data); }, readfile : function(path,recall){  fs.readFile(path,function(err,data){   if(err){    console.log('读取文件分线程错误:'+err);    recall('文件不存在');   }else{    recall(data);   }  })  console.info('异步读取文件方法执行了'); }, readImg : function(path,res){  fs.readFile(path,'binary',function(err,imgdata){   if(err){    console.log('读图片分线程错误:'+ err);    return;   }else{    res.write(imgdata,'binary');    res.end('');   }  }) }}