NodeJS_07

NodeJS七天课程学习笔记_第7天综合案例

课程内容概要:

1.  介绍path模块的api

2. 重点介绍path.join方法 与 __dirname

3. 介绍xheditor编辑器的使用(包括上传图片)

4. 介绍formidable中间件处理上传的图片

提了一下 编辑器默认的是: 15号字体 因为大小最合适
讲了一下chrome的插件: editthiscookie
不要担心模块重复加载性能问题,因为模块加载是有缓存的

中间件midware 就是一个方法(包装)

提了一下,使用 postman 模拟请求

提了一下 chrome插件: cors toggle

喊了一下口号: 热爱编程,享受生活

推荐了「深入理解ES6」(尼古拉斯·泽卡斯)、「JavaScript高级程序设计」(小孩望远镜)、

总结了MongoDB数据库的特点:
1. 灵活
2. 不用建表
3. 业务的改动不用关心表结构
4. DBA、架构师 都要掌握(设计、维护、分布式计算)

总结了Mongoose的使用方法:
1. 虽然mongodb官方包也可以操作MongoDB数据库,但是…
2. mongoose开发者是 WordPress团队
3. 设计Schema
4. 生成Model(模型构造函数)
5. CRUD (支持Promise.then语法)

总结了Promise的使用方法:
1. 介绍了什么是回调地狱(回调函数中层层嵌套回调函数)
2. ECMAScript6 中新增了一个API: Promise
3. Promise相当于一个容器(同步执行),一旦创建会立即执行里面的代码(通常是异步操作的代码块block)
4. 异步任务 初始状态是pending
5. 最终状态只能是成功(resolve)或者失败(reject)
6. then方法的参数1是成功的结果(resolveCallback)
7. then方法的参数2是失败的结果(rejectCallback)
8. 可以在then方法中返回另一个promise对象,这样就可链式调用then方法

在Node的命令行中,介绍了一下path模块的常用API

如图所示:

介绍一下path路径操作模块
1. path.basename 获取带扩展名的 文件名,如index.html
2. path.dirname  获取目录部分,如”/Users/beyond/sg_node”
3. path.extname  获取扩展名部分,如”.html”
4. 强大的path.parse方法,将一个路径转成对象
例如: /Users/beyond/sg_node/index.html
root: “/” 表示 根目录
name: “index” 表示不带后缀的文件名
ext: “.html” 表示后缀名(带点号)
base: “index.html” 表示带后缀的文件名
dir: “/Users/beyond/sg_node” 表示 目录部分
5. 强大的path.join方法
进行多个路径拼接(能智能处理多写或少写 / )

6. path.isAbsolute 判断一个路径是不是绝对路径

7. 强大path.resolve方法,如下图所示

重点来了!
Node中引入自定义模块时的路径标识符就只是相对于当前文件的路径,
与 将来node命令执行时 命令行所在的路径无关!!!
例如:
require(‘./router’)
require(‘./dao’)
这个自定义模块的路径,就只是相对于当前的文件的路径而言的
与 将来node命令执行时 命令行所在的路径无关!!!

区别,注意:
fs.readFile(‘a.txt’)
在readFile读取文件时,这个相对路径,相对的可是将来 Node命令执行时 命令行所在的路径喔~~~
因此,在读写文件时,使用相对路径是不安全的,
因为Node设计为: 读写文件的相对路径只是相对于 Node命令执行时,命令行所在的路径

因此,为了把文件读取时,这个相对路径,转成绝对路径,
我们隆重介绍Node中除了require和exports之外的另外两个非常重要的成员:
__dirname 它可动态获取当前模块所处目录的绝对路径
__filename 它可动态获取当前模块的绝对路径

特别注意:
__dirname和__filename 与 将来node执行时 命令行所在的路径 无关的

当然在把相对路径转换成绝对路径的时候,为了避免手动拼接路径出错,我们推荐使用path.join方法喔~

在开始blog综合案例之前,先讲了一下如何使用path.join(__dirname,’public’)生成绝对路径,

以保证任何情况下,目录都是正确路径

node_48的目录情况如下:

示意图就是这样的:

node_48_1.js代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

// 演示readFile中相对路径 是相对于 node命令执行时,命令行所处的路径
require('./subfolder/node_48_2.js')

node_48_2.js代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

// 演示readFile中相对路径 是相对于 node命令执行时,命令行所在的路径
fs = require('fs')
// readFile相对路径 不安全, 因为它是相对于 node命令执行时,命令行所在路径
fs.readFile('./node_48_3.txt',function (error,data) {
		if (error) {
			NSLog('读取失败: ' + error)
		}else{
			NSLog('读取成功: \n' + data)
		}
		
})

我们先来 正确执行一次 ,即:

1. 将命令行 cd 到subfolder目录中

2. node命令 执行 上一级目录中的node_48_1.js文件

效果如下:

下面,我们直接在node_48目录下执行该目录下的node_48_1.js

由于 node_48_1.js中require进来了子目录subfolder中的node_48_2.js

而node_48_2.js使用相对路径读取的是同级目录subfolder中的node_48_3.txt

但是: readFile中相对路径设计之初就 是 相对于 执行Node命令时,所处路径

而我们这时, 是在node_48目录下 执行的node命令, 而不是 subfolder目录了

因此,报错: 在node_48目录中找不到node_48_3.txt,如图所示:

解决办法如下:

使用Node提供的__dirname和path.join方法 把readFile中的相对路径,改成绝对路径就好了

node_48_2.js代码如下

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

// 演示readFile中相对路径 是相对于 node命令执行时,命令行所在的路径
fs = require('fs')
path = require('path')
// readFile相对路径 不安全, 因为它是相对于 node命令执行时,命令行所在路径
// fs.readFile('./node_48_3.txt',function (error,data) {

// 通过	Node提供的__dirname和path.join,将相对路径转成绝对路径
fs.readFile(path.join(__dirname,'./node_48_3.txt'),function (error,data) {
		if (error) {
			NSLog('读取失败: ' + error)
		}else{
			NSLog('读取成功: \n' + data)
		}
		
})

这时,无论你在哪个路径下通过node 命令执行 node_48_1.js都能成功读取node_48_3.txt的内容了

效果如下:

下面的node_49.js 简单演示了一下node提供的__dirname和__filename输出结果:

node_49.js代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  

NSLog(__dirname,false)
NSLog(__filename)

(注意: __dirname和__filename 千万不要使用 字符串拼接)

效果如下:

在开始blog正式项目之前,我们还要先讲一下

如何  用使用富文本编辑器xheditor来上传文件

以及 如何使用中间件formidable 处理上传的头像图片

总体效果如下:

先说说xheditor的使用方法

官网:xheditor.com

第1步, 下载(我这儿下载的是1.4M大小的V1.2.2版本)

第2步, 解压

第3步, 拷贝 3个文件夹, 3个js文件 到public/js目录下

表情目录: xheditor_emot

皮肤目录: xheditor_skin

插件目录: xheditor_plugins

jquery文件: jquery1.4.4.js

语言包文件: xheditor-zh-cn.js

核心JS文件: xheditor-1.2.2.min.js

如下图所示

第4步,使用xheditor

这儿我们使用的是node_50_edit.html作为上传页面

步骤1:  严格按顺序 引入public/js目录下的 xheditor 相关的3个js文件

        <!-- 第1步. 必须先引入 jquery 1.4.4 -->
        <script type="text/javascript" src="public/js/jquery1.4.4.js">
        </script>
        <!-- 第2步. xheditor的核心JS -->
        <script type="text/javascript" src="public/js/xheditor-1.2.2.min.js">
        </script>
        <!-- 第3步.  xheditor 支持中文 -->
        <script type="text/javascript" src="public/js/xheditor-zh-cn.js">
        </script>

步骤2: 创建表单,里面有一个textarea, id为 id_xheditor (后面要用这个id查找并初始化)

        <div style="margin:0 auto;text-align:center;padding-left:50px;padding-right:50px;">

            <form method="post" action="xxx.php">
                <!-- 第4步. xheditor所使用的id -->
                <textarea id="id_xheditor" rows="12" cols="80" style="width: 100%;height:240%;">
                        未闻花名 vwhm.net
                </textarea>
            </form>
            
        </div>

步骤3: 在document.ready方法中, 根据textarea的id_xheditor查找,并进行初始化

            $(document).ready(function () {
                var uploadUrl = "uploadfile"
                // 第5步. 图片上传配置
                $('#id_xheditor').xheditor({
                    // 上传图片
                    upImgUrl: uploadUrl,
                    // 支持的图片后缀
                    upImgExt: "jpg,jpeg,gif,png",
                    // 上传回调函数
                    onUpload: uploadCompleteFunction,
                    // 多文件上传
                    upMultiple: true,

                    // 默认皮肤
                    skin: 'default',
                    // 工具栏样式
                    tools: 'full',

                    upLinkUrl:uploadUrl,
                    upLinkExt:"zip,rar,txt",

                    upFlashUrl:uploadUrl,
                    upFlashExt:"swf",

                    upMediaUrl:uploadUrl,
                    upMediaExt:"avi,mp4,wmv,flv"
                })
            })

步骤4: 做做样子,实现一下 上传回调函数(初始化的时候不写回调,那就不实现 也不影响)

            // 第6步. 上传回调函数,参数是服务器返回的 图片url(例如:upload/beyond.jpg)
            function uploadCompleteFunction (responseMsg) {
                NSLog('上传回调: ' + responseMsg)
            }

完整的node_50_edit.html代码如下:

<!DOCTPYE html>  
<html lang="zh">  
<head>  
    <link rel="icon" href="public/img/beyond.jpg" type="image/x-icon"/>
    <meta charset="UTF-8">
    <meta name="author" content="beyond">
    <meta http-equiv="refresh" content="520">
    <meta name="description" content="未闻花名-免费零基础教程-beyond">
    <meta name="viewport" content="width=device-width, 
    initial-scale=1.0, maximum-scale=1.0,minimum-scale=1.0,user-scalable=0" />
    <meta name="keywords" content="HTML,CSS,JAVASCRIPT,JQUERY,XML,JSON,C,C++,C#,OC,PHP,JAVA,JSP,PYTHON,RUBY,PERL,LUA,SQL,LINUX,SHELL,汇编,日语,英语,泰语,韩语,俄语,粤语,阿语,魔方,乐理,动漫,PR,PS,AI,AE">
    <title>beyond心中の动漫神作</title>
    <link rel="stylesheet" type="text/css" href="public/css/beyondbasestylewhite5.css">
    <script type="text/javascript" src="public/js/nslog.js"></script>

    <!--[if lt IE 9]>
        <script src="//apps.bdimg.com/libs/html5shiv/3.7/html5shiv.min.js"></script>
        <script type="text/javascript" src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.js">
        </script>
        <![endif]-->

        <style type="text/css">
            body{
                font-size: 100%; 
                /*声明margin和padding是个好习惯*/  
                margin: 0;  
                padding: 0; 
                background-image: url("public/img/sakura4.png");  
                background-repeat: no-repeat;  
                background-position: center center;  
            }
        </style>
        <!-- 绿色按钮的css效果 -->
        <link rel="stylesheet" type="text/css" href="public/css/beyondbuttongreen.css">

        <!-- 第1步. 必须先引入 jquery 1.4.4 -->
        <script type="text/javascript" src="public/js/jquery1.4.4.js">
        </script>
        <!-- 第2步. xheditor的核心JS -->
        <script type="text/javascript" src="public/js/xheditor-1.2.2.min.js">
        </script>
        <!-- 第3步.  xheditor 支持中文 -->
        <script type="text/javascript" src="public/js/xheditor-zh-cn.js">
        </script>
        

        


    </head>  

    <body>  
        <h1 style="color:white;text-shadow:2px 2px 4px #000;letter-spacing:5px;" class="sgcontentcolor sgcenter">  
            未闻花名
        </h1>

        <div style="margin:0 auto;text-align:center;padding-left:50px;padding-right:50px;">

            <form method="post" action="xxx.php">
                <!-- 第4步. xheditor所使用的id -->
                <textarea id="id_xheditor" rows="12" cols="80" style="width: 100%;height:240%;">
                        未闻花名 vwhm.net
                </textarea>
            </form>

        </div>

        <p class="sgcenter">
          <b>注意: </b>NodeJS + formidable中间件 + xheditor编辑器
      </p>

      <footer id="copyright">
        <p style="font-size:14px;text-align:center;font-style:italic;">  
            Copyright © <a id="author">2018</a> Powered by <a id="author">beyond</a>  
        </p>        
    </footer>

    <script type="text/javascript">
    /*
重要说明:
1,上传文件域的名字 必须为:filedata
2,返回结构必需为json,并且结构必须如下:
完整的图片url是 http://localhost:5267/upload/beyond.jpg
{"err":"","msg":"upload/beyond.jpg"}
若上传出现错误,请将错误内容保存在err变量中;
若上传成功,请将服务器上的绝对或者相对地址保存在msg变量中。
编辑器若发现返回的err变量不为空,则会弹出窗口显示返回的错误内容
*/
            
            $(document).ready(function () {
                var uploadUrl = "uploadfile"
                // 第5步. 图片上传配置
                $('#id_xheditor').xheditor({
                    // 上传图片
                    upImgUrl: uploadUrl,
                    // 支持的图片后缀
                    upImgExt: "jpg,jpeg,gif,png",
                    // 上传回调函数
                    onUpload: uploadCompleteFunction,
                    // 多文件上传
                    upMultiple: true,

                    // 默认皮肤
                    skin: 'default',
                    // 工具栏样式
                    tools: 'full',

                    upLinkUrl:uploadUrl,
                    upLinkExt:"zip,rar,txt",

                    upFlashUrl:uploadUrl,
                    upFlashExt:"swf",

                    upMediaUrl:uploadUrl,
                    upMediaExt:"avi,mp4,wmv,flv"
                })
            })

            // 第6步. 上传回调函数 (初始化的时候不写回调,那就不实现 也不影响) 
            // 参数是服务器返回的 图片url(例如:upload/beyond.jpg)
            function uploadCompleteFunction (responseMsg) {
                NSLog('上传回调: ' + responseMsg)
            }
        </script>
</body>  
</html>  

说完了xheditor的使用方法,接下来说说如何使用用来处理上传文件的 中间件 formidable

官网的使用文档: npmjs.com/package/formidable#readme

其他的几个也可以实现表单中上传文件的解析

第1步, 安装formidable:

npm install formidable –save

第2步, 配置

// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')  // 只是调试用
var path = require('path')

第3步, 使用

重要说明:

重要说明:
1,上传文件域的名字 必须为:filedata

2,返回结构必需为json,并且结构必须如下:

// 完整的图片url是 http://localhost:5267/upload/beyond.jpg

{“err”:””,”msg”:”upload/beyond.jpg”}
若上传出现错误,请将错误内容保存在err变量中;
若上传成功,请将服务器上的绝对或者相对地址保存在msg变量中。
编辑器若发现返回的err变量不为空,则会弹出窗口显示返回的错误内容

// ----------------上传一张图片-------------------
router.post('/uploadfile',function (request,response) {
	// 使用formidable中间件
	var form = new formidable.IncomingForm();
	// 配置 上传保存路径 (应该将日期计算进来)
	form.uploadDir = '/Users/beyond/sg_node/node_27/uploads'
	// 让formidable中间件 解析上传的图片
    form.parse(request,function (error,fields,files) {
    	
    	// var uploadObj = util.inspect({
    	// 	fields: fields,
    	// 	files: files
    	// })

    	// 保存的完整路径
    	var uploadSavePath = files['file']['path']
    	var pathObj = path.parse(uploadSavePath)
    	// 文件名,不带后缀
    	var filePlainName = pathObj.name

    	// 回写给xheditor用的成功的json,格式必须这样写
    	// {"err":"","msg":"uploads/beyond.jpg"}
    	var jsonObj = {
    		err: "",
    		msg: 'uploads/' + filePlainName
    	}
    	// 回写给浏览器
    	response.writeHead(200, {'content-type': 'text/json'});
     	response.end(JSON.stringify(jsonObj));
    })
})

node_50_index.js这个APP入口文件完整的代码如下:

function NSLog(loli,needLogo=true) {console.log(loli);if(needLogo){console.log('\nCopyright © 2018 Powered by beyond')};}  


// 导入框架
var express = require('express')
var path = require('path')
// 创建服务器对象
var appServer = express()
// 监听端口,并启动服务
appServer.listen(5267,function (error) {
	if (error) {
		return NSLog('启动失败: ' + error)
	}
	NSLog('服务启动成功')
})
// -----------------------------------

// 静态资源请求时的 staticFileUrlPrefix
var staticFileUrlPrefix = '/public/'
// var staticFileUrlPrefix = '/public' 

// 访问也只能使用 localhost:5267/public/img/beyond.jpg
// 磁盘上的静态资源目录
var staticFilePath = './public/'   
// var staticFilePath = 'public' 

var callbackFunction = express.static(staticFilePath)
appServer.use(staticFileUrlPrefix,callbackFunction)
// 再开一个静态资源目录
appServer.use('/uploads/',express.static(path.join(__dirname,'uploads')))

// -----------------------------------

// 指明:对于 所有后缀为html 的模板文件 使用模板引擎
var templateFileSuffix = 'html'
appServer.engine(templateFileSuffix,require('express-art-template'))
// 下面这一句参数配置,可有可无
appServer.set('view options',{
	debug: process.env.NODE_ENV !== 'production'
})
// 注意:如果不想把模板文件放在默认的views目录下,则可以通过下面代码更改设置
// appServer.set('views','其他目录')

// -----------------------------------
// 使用middleware中间件body-parser进行post请求体中数据解析
var bodyParser = require('body-parser')
// 设置解析 application/x-www-form-urlencoded
appServer.use(bodyParser.urlencoded({extended: false}))        
// 设置解析 application/json
appServer.use(bodyParser.json())

// -----------------------------------
// 自定义路由设计的目的是:
// 1.让主入口程序的职责更加单一,代码更加简洁
//     1.1 创建服务
//     1.2 做一些服务相关的配置,比如:
//           1.2.1 静态资源配置
//           1.2.2 模板引擎配置
//           1.2.3 body-parse 解析表单
//           1.2.4 挂载自定义路由
//           1.2.5 监听端口,启动服务
// 使用自定义的路由模块 必须使用./
// 注意: 配置模板引擎和body-parser, 一定要在挂载路由之前
var beyondRouter = require('./node_50_router')
appServer.use(beyondRouter)

node_50_router.js这个路由文件完整的代码如下:

function NSLog(loli) {console.log(loli);return 'Copyright © 2018 Powered by beyond';};  
/*
	自定义路由模块的职责是:
		专门处理所有的路由
		根据不同的请求方式和路径,采取相应的处理方法
*/ 
// express 专门提供了路由的处理方法
var express = require('express')

// ---------------使用formidable解析上传的图片--------------------
var formidable = require('formidable')
// var util = require('util')
var path = require('path')
// -----------------------------------

// 1.使用express专门提供的路由器处理路由 
var router = express.Router()

// -----------------------------------
// 时间格式化
// var BeyondDateFormatFunction = require('./BeyondDateFormat')

// ----------------首页-------------------
router.get('/',function (request,response) {
	response.render('index/node_50_edit.html')
})

// ----------------上传一张图片-------------------
router.post('/uploadfile',function (request,response) {
	// 使用formidable中间件
	var form = new formidable.IncomingForm();
	// 配置 上传保存路径 (应该将日期计算进来)
	form.uploadDir = '/Users/beyond/sg_node/node_27/uploads'
	// 让formidable中间件 解析上传的图片
    form.parse(request,function (error,fields,files) {
    	
    	// var uploadObj = util.inspect({
    	// 	fields: fields,
    	// 	files: files
    	// })

    	// 保存的完整路径
    	var uploadSavePath = files['file']['path']
    	var pathObj = path.parse(uploadSavePath)
    	// 文件名,不带后缀
    	var filePlainName = pathObj.name

    	// 回写给xheditor用的成功的json,格式必须这样写
    	// {"err":"","msg":"uploads/beyond.jpg"}
    	var jsonObj = {
    		err: "",
    		msg: 'uploads/' + filePlainName
    	}
    	// 回写给浏览器
    	response.writeHead(200, {'content-type': 'text/json'});
     	response.end(JSON.stringify(jsonObj));
    })
})
// 3.在模块文件最后,导出router
module.exports = router

整个NodeJS + formidable中间件 + xheditor上传图片的效果如下:

明天开始正式的blog综合项目

 

未完待续,下一章节,つづく