资讯专栏INFORMATION COLUMN

Node.js开发系列(六)

Jiavan / 762人阅读

摘要:在项目的文件夹下完成安装依赖项。的主要功能是负责创建一个服务,然后对请求的路由进行判断和过滤,交由模块进行处理具体的请求路由。

上一节我们实现了手动控制路由的示例,这节我们来做一个完整的示例

目录结构
基于前面的一些铺垫,这节会做一个完整的示例。目录的文件结构如下 :


 - 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.js
start.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:UsersADMINI~1AppDataLocalTempupload_5990006963ce4f2
9de485ddadd819355",
        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.jsroute()方法的实现,完整的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".




    
    HOME


    

This is Home

File Manager


Show All Files

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 = "" +
            "

File Manager

"; //有文件就生成文件列表 if (list.length) { html += "
    "; for (i = 0; i < list.length; i++) { html += "
  • " + list[i] + "
  • "; } html += "
"; } else { //没有文件 html += "

No files found

"; } 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处理表单,文件上传和下载的示例。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/86889.html

相关文章

  • Node.js开发系列

    摘要:在项目的文件夹下完成安装依赖项。的主要功能是负责创建一个服务,然后对请求的路由进行判断和过滤,交由模块进行处理具体的请求路由。 上一节我们实现了手动控制路由的示例,这节我们来做一个完整的示例 目录结构 基于前面的一些铺垫,这节会做一个完整的示例。目录的文件结构如下 :showImg(https://segmentfault.com/img/bVKraD?w=186&h=214); ...

    tracy 评论0 收藏0
  • WebAssembly 系列(一)生动形象地介绍 WebAssembly

    摘要:但是为什么执行的更快呢在这个系列文章中,我会为你解释这一点。所以当人们说更快的时候,一般来讲是与相比而言的。被人们广为传播的性能大战在年打响。性能的提升使得的应用范围得到很大的扩展。现在通过,我们很有可能正处于第二个拐点。 作者:Lin Clark 编译:胡子大哈 翻译原文:http://huziketang.com/blog/posts/detail?postId=58ce8036...

    wangbjun 评论0 收藏0
  • 大前端2018现在上车还还得及么

    摘要:面向对象三大特征继承性多态性封装性接口。第五阶段封装一个属于自己的框架框架封装基础事件流冒泡捕获事件对象事件框架选择框架。核心模块和对象全局对象,,,事件驱动,事件发射器加密解密,路径操作,序列化和反序列化文件流操作服务端与客户端。 第一阶段: HTML+CSS:HTML进阶、CSS进阶、div+css布局、HTML+css整站开发、 JavaScript基础:Js基础教程、js内置对...

    stormgens 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<