当前位置: 首页 > 后端技术 > Node.js

Node.js开发系列(六)

时间:2023-04-03 14:38:51 Node.js

上一节我们实现了手动控制路由的示例,这节我们来做一个完整的示例目录结构 基于前面的一些铺垫,这节会做一个完整的示例。目录的文件结构如下 : - node-express-pug ==>项目目录 - package.json ==>依赖文件 - server.js ==> 入口文件 - start.js ==> 创建服务 - router.js ==> 路由中转 - handlers.js ==> 路由处理 - views/home.html ==>首页 - files/ ==>存放上传文件的目录 - node_modules/ ==>依赖项文件目录 package.json首先我们安装依赖项,本示例中的package.json文件如下:{ "name": "application-name", "version": "0.0.1", "dependencies": { "formidable": "latest", "mime": "~1.3.4" }}其中,formidable用于处理form表单数据,mime是一个互联网标准类型,通过设定它就可以设定文件在浏览器的打开方式。稍后我们会看到如何使用它们。在项目的文件夹下npm install完成安装依赖项。server.js首先我们来实现server.js。在server.js中,我们主要实现服务器的入口功能,调用各个模块组件,由其他模块实现具体的功能。var server = require('./start');var router = require('./router');var handlers = require('./handlers');var handler = {};handler[['/','GET']] = handlers.home;handler[['/show','GET']] = handlers.show;handler[['/upload','POST']] = handlers.upload;server.start(router.route,handler);先导入我们创建的其他的模块文件,然后定义一个handlder对象,这里配置了对应的路由路径和其对应的处理方法,类似handler[['/','GET']] = handlers.home;,然后又调用了server模块的start()方法,并传入两个参数,分别是router.route方法和handler对象。下面我们就来实现start.js文件。start.jsstart.js的主要功能是负责创建一个server服务,然后对请求的路由进行判断和过滤,交由router模块进行处理具体的请求路由。完整的代码如下//1.引用模块var http = require('http');var url = require('url');var formidable = require('formidable');var querystring = require('querystring');//2.start(route,handler)方法//方法的参数是由上层传递过来,分别是router.route方法和handler对象//其中route负责处理路由//handler对象里是我们预先定义的可用处理的路由和对应的请求类型function start(route,handler) { console.log("Start Begin"); //3.监听1337端口,创建服务器 var port = process.env.port || 1337; http.createServer(onRequest).listen(port); //4.创建服务器的回调方法 function onRequest(req, res) { console.log("Request Begin"); //解析请求的路径名 var pathname = url.parse(req.url).pathname; var query = url.parse(req.url).query; //POST方法处理 if (req.method === 'POST'){ //解析form表单POST方式提交数据 var form = new formidable.IncomingForm(); //解析路径的请求参数,包装成data向下传递 //function (err, fields, files)是解析成功的回调方法 form.parse(req, function (err, fields, files) { if (err){ console.error(err.message); return; } var data = {fields:fields, files:files}; execute(pathname,handler,req, res, data); }); } //GET方法处理请求 if (req.method === 'GET'){ var data = { //解析路径的请求参数,包装成data向下传递 fields: querystring.parse(query) }; execute(pathname,handler,req, res, data); } console.log("Request End"); } //5.执行处理后的请求 function execute(pathname, handler, req, res, data) { //route执行返回值,如果发生错误,统一返回400 var content = route(pathname,handler,req,res,data); if (!content){ res.writeHead('400',{ "Content-Type":"text/plain" }); console.log(req.url); res.write("400 Bad Request"); res.end(); } }}//6.导出模块exports.start = start;这里注意var form = new formidable.IncomingForm();form.parse(req, function (err, fields, files) { });form.parse会解析出很多的属性,传给回调参数files,下面是一个我上传文件后打印的请求参数,参考如下:{ fields: {}, files: { fn: File { domain: null, _events: {}, _eventsCount: 0, _maxListeners: undefined, size: 4510, path: 'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\upload_5990006963ce4f29de485ddadd819355', name: 'sql.sql', type: 'application/octet-stream', hash: null, lastModifiedDate: 2017-03-10T07:00:14.577Z, _writeStream: [Object] } } }router.js我们看到最后的处理交给了var content = route(pathname,handler,req,res,data);,那么下面我们就来看router.js中route()方法的实现,完整的router.js如下:function route(pathname,handler,req,res,data) { console.log('Route'); var method = req.method; if (typeof handler[[pathname,method]] === 'function'){ return handler[[pathname,method]](res, data); }else { console.log("No Method found for " + pathname); return null; }}exports.route = route;还记得我们的handler对象的格式吗?我们的参数传入了解析后的pathname,在这里又解析了请求的方式var method = req.method;。typeof handler[[pathname,method]] === 'function',如果请求路由和请求不在我们的handler对象的中,即没有指定的方法调用,那么我们就直接返回null,如果有,就调用这个方法。打个比方handler[['/show','get']](res, data)等同于handlers.show(res, data)。home.html在home.html中我们定义了简单的form表单,实现一个上传文件的功能。其中上传文件的input标签的name="fn".<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>HOME</title></head><body> <p>This is Home</p> <h1>File Manager</h1> <hr> <a href="/show">Show All Files</a> <form action="/upload" method="post" enctype="multipart/form-data" > <input type="file" name="fn"> <input type="submit" value="Upload File"> </form></body></html>handlers.js这里才是我们对各个正确路由的响应。以下是完整的代码,我会逐一解释说明。//1.引入模块var fs = require('fs');var path = require('path');var mime = require('mime');//2.home方法,即浏览器请求`http://127.0.0.1:1337`的方法//这里我们直接读取views/home.html文件,返回将页面展现出来function home(res, data) { fs.readFile('views/home.html', function (err, data) { res.writeHead(200, {"Content-Type": "text/html"}); //注意这里的data并不是home的参数,而是读取文件成功后的回调data res.write(data); res.end(); }); return true;}//3.show方法 //show方法会处理两种类型的请求,两种类型的请求都是get类型//data.fields && data.fields['fn'] 这里处理的有上传的文件后,点击文件的链接下载文件function show(res, data) { //解析参数的data的值,这里用fn是因为后面form表单中定义的值是fn if (data.fields && data.fields['fn']){ //获取文件名称 var name = data.fields['fn']; //取得文件完整名称 var file = path.join(__dirname, '/files', name); //通过文件名指定mime类型 var mimeType = mime.lookup(file); //设定响应头 res.setHeader('Content-disposition','attachment;filename=' + name); res.setHeader('Content-Type',mimeType); //读取文件数据 var fileData = fs.readFileSync(file,'binary'); //响应给给用户 res.end(fileData, 'binary'); //这里会直接返回,并不会走下面的方法 return true; } //如果用户不是点击的下载链接进行show方法 //那么就读取文件列表显示在界面上,并且提供下载功能 fs.readdir('files', function (err, list) { console.log(list); res.writeHead(200, {"Content-Type": "text/html"}); var html = '<html><head></head>' + '<body><h1>File Manager</h1>'; //有文件就生成文件列表 if (list.length) { html += "<ul>"; for (i = 0; i < list.length; i++) { html += '<li><a href="/show?fn=' + list[i] + '">' + list[i] + '</a></li>'; } html += "</ul>"; } else { //没有文件 html += '<h2>No files found</h2>'; } html += '</body></html>'; res.write(html); res.end(); }); return true;}//4.上传文件的方法,方法是响应home.html中的form表单function upload(res, data) { var temp = data.files['fn'].path; var name = data.files['fn'].name; //调用复制文件的方法 copyFile(temp,path.join('./files',name),function (err) { if (err){ console.log(err); return false; }else { return true; } });}//定义复制文件的方法function copyFile(source, target, callback) { //读文件流 var rs = fs.createReadStream(source); rs.on('error',function (err) { callback(err); }); //写文件流 var ws = fs.createWriteStream(target); ws.on('error',function (err) { callback(err); }); ws.on('finish',function () { callback(); }); //写入,并覆盖源文件的内容 rs.pipe(ws);}//导出方法exports.home = home;exports.show = show;exports.upload = upload;小结在项目文件夹下node server启动http服务器,在浏览器中输入http://127.0.0.1:1337在上传文件后点击Show All Files,可以看到文件列表,点击其中一个,即可下载。这样我们就完成了一个完整的node处理表单,文件上传和下载的示例。