Skip to content

Express框架范例

Express

Express是基于Node.js 平台,快速、开放、极简的Web开发框架。

初始化环境

MacintoshdeMacBook-Pro-139:demo elasticnotes$ ls
MacintoshdeMacBook-Pro-139:demo elasticnotes$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (demo) 
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /Users/elasticnotes/Desktop/demo/package.json:

{
  "name": "demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes) 
MacintoshdeMacBook-Pro-139:demo elasticnotes$ ls
package.json

安装express

MacintoshdeMacBook-Pro-139:demo elasticnotes$ npm install express --save

安装nodemon

MacintoshdeMacBook-Pro-139:demo elasticnotes$ npm install nodemon --save

Nodemon

nodemon 是一种工具,可在检测到目录中的文件更改时通过自动重新启动节点应用程序来帮助开发基于 node.js 的应用程序。

  • 自动重新启动应用程序。
  • 检测要监视的默认文件扩展名。
  • 默认支持 node,但易于运行任何可执行文件,如 python、ruby、make 等。
  • 忽略特定的文件或目录。
  • 监视特定目录。
  • 使用服务器应用程序或一次性运行实用程序和 REPL。
  • 可通过 Node require 语句编写脚本。
  • 开源,在 github 上可用。

修改package.json,调整启动文件

1
2
3
4
"scripts": {
    "start": "nodemon ./server.js",
    "test": "echo \"Error: no test specified\" && exit 1"
},

启动文件与层级目录关系

|- public
|- server.js
|- package.json
|- package-lock.json

编写server.js,实现访问静态资源文件

1
2
3
4
const express = require('express');
const app = express();
app.use('/', express.static('public'));
app.listen('填入自定义端口号');

以上 就上线了简单的web服务功能,可访问静态资源文件。以下内容为启停与扩展内容。

守护进程启动 & 一般关闭

MacintoshdeMacBook-Pro-139:demo elasticnotes$ nohup npm start > nohup.log 2>&1 &
MacintoshdeMacBook-Pro-139:demo elasticnotes$ ps aux|grep node|grep server.js|awk '{print $2}' |xargs kill -9

server.js 扩展(实现POST访问请求与图像处理)

const express = require('express');
const bodyParser = require('body-parser');
const config = require('./config/config');
const Ebag = require('./ebag');
const jpeg = require('jpeg-js');
const base64 = require('base64-js');
const cv = require('opencv');
const fs = require('fs');
const app = express();

let g_ebag = new Ebag();
let g_app = {topic_show_list: ''};

app.use(bodyParser.urlencoded({extended: false, limit: '20mb'}));

app.use('/', express.static('public'));

app.post('/data', function (req, res) {
    let bucket = req.body.bucket;
    let dir = req.body.dir;
    let topicList = req.body.topiclist;
    let topicConf = [];
    let initTopiclist = [];

    if (bucket === '0' || dir === '0') {
        res.send({'data': [{'name': 'Please', 'value': [-1, Date.now() - 1, '']},
        {'name': 'Please', 'value': [-1, Date.now(), '']}],
        'list': ['Please', 'enter', 'cloud', 'information', '.']});
        return;
    }

    if (topicList !== '') {
        topicConf = topicList.substr(0, topicList.length - 1).split(',');
    }
    g_ebag.open_data(bucket, dir, err => {
        if (err) {
            let result = {'err': -1, 'msg': '读取文件失败,请检查bucket与资源目录地址是否有误'};
            res.send(result);
        } else {
            // 数据 x/y 数据
            g_ebag.topic_msg = {};
            for (let msg of g_ebag.op_blocks.message) {
                let topic = msg.head.topic;
                if (!g_ebag.topic_msg[topic]) {
                    g_ebag.topic_msg[topic] = [];
                }
                g_ebag.topic_msg[topic].push(msg);
            }
            g_app.topic_show_list = Object.keys(g_ebag.topic_msg).sort();
            // 显示图表
            let topic_index = {};
            let data = [];
            let idx = 0;
            // 如果topiclist不为空 计算与g_app.topic_show_list的交集 过滤展示的topic
            if (topicConf.length !== 0) {
                /**
                 * 重组选择消息列表 已选择 为checked
                 */
                g_app.topic_show_list.forEach(function (value, index) {
                    initTopiclist[index] = {
                        'name': value,
                        'checked': (topicConf.indexOf(value) === -1) ? 0 : 1
                    };
                });
                g_app.topic_show_list = topicConf.filter(function (val) {
                    return g_app.topic_show_list.indexOf(val) > -1;
                });
            } else {
                g_app.topic_show_list.forEach(function (value, index) {
                    initTopiclist[index] = {'name': value, 'checked': 0};
                });
            }

            // 画图参数 及 自定义echarts数据组装
            g_app.topic_show_list.forEach(function (topic, index) {
                topic_index[topic] = idx++;
                for (let msg of g_ebag.topic_msg[topic]) {
                    let tm = Number(msg.head.time);
                    data.push({
                        name: topic,
                        value: [
                            topic_index[topic],
                            tm,
                            msg.pos
                        ]
                    });
                }
            });
            // 获取topic信息的data
            let topicData = g_ebag.op_blocks.data;
            // 返回图片信息
            let topicPic = {};
            /* bca-disable */
            for (let key in topicData) {
                // 有图片数据 需要处理图片相应的数据
                switch (topicData[key].type) {
                    case 'baidu.pavaro.CameraDataPb': // BGR -> RGB -> jpeg -> base64
                        let img = {
                            data: [],
                            height: topicData[key].decode_msg.height,
                            width: topicData[key].decode_msg.width,
                        };
                        for (let i = 0, j = 0; i < topicData[key].decode_msg.data.length; i += 3, j += 4)
                        {
                            img.data[j + 0] = topicData[key].decode_msg.data[i + 2];
                            img.data[j + 1] = topicData[key].decode_msg.data[i + 1];
                            img.data[j + 2] = topicData[key].decode_msg.data[i + 0];
                            img.data[j + 3] = 0xff;
                        }
                        let rawImageData = {
                            data: img.data,
                            width: topicData[key].decode_msg.width,
                            height: topicData[key].decode_msg.height
                        };
                        let jpg_data_a = jpeg.encode(rawImageData, 50);
                        let jpg_data_base_a = base64.fromByteArray(jpg_data_a.data);
                        topicPic[key] = jpg_data_base_a;
                        delete topicData[key].decode_msg.data;
                        break;

                    case 'baidu.pavaro.CameraCompressDataPb':
                        let raw = jpeg.decode(topicData[key].decode_msg.data, true);
                        for (let i = 0; i < raw.data.length; i += 4) {
                            let t = raw.data[i + 0];
                            raw.data[i + 0] = raw.data[i + 2];
                            raw.data[i + 2] = t;
                        }
                        let jpg_data_b = jpeg.encode(raw, 50);
                        let jpg_data_base_b = base64.fromByteArray(jpg_data_b.data);
                        topicPic[key] = jpg_data_base_b;
                        delete topicData[key].decode_msg.data;
                        break;

                    default:
                        break;
                }
            }

            let result = {
                'err': 0,
                'data': data,
                'list': g_app.topic_show_list,
                'topic': topicData,
                'img': topicPic,
                'initTopiclist': initTopiclist
            };
            res.send(result);
        }
    });
});

app.post('/mark', function (req, res) {
    let obstacle = JSON.parse(req.body.perdata);
    let freespace = JSON.parse(req.body.freedata);
    let imgData = req.body.obdata;
    let date = Date.now();

    // base64 存储到 文件
    let path = './public/sourceimg/' + date + '.jpeg';

    // 过滤data:URL
    let base64Data = imgData.replace(/^data:image\/\w+;base64,/, '');
    let dataBuffer = Buffer.from(base64Data, 'base64');
    fs.writeFile(path, dataBuffer, function (err) {
        if (err) {
            res.send({'code': '-1', 'msg': '图片处理失败!'});
        }

        // 障碍物标记 - 输出图片
        let outputPath = './public/obstaclesimg/' + date + '.jpeg';

        // 处理图片
        /* bca-disable */
        cv.readImage(path, function (err, im) {

            // 标记障碍物信息
            obstacle.forEach(function (value, index) {
                im.rectangle([value.imageX, value.imageY], [value.imageW, value.imageH], [0, 255, 0], 2);
            });

            // 标记可行驶区域信息
            /* bca-disable */
            freespace.forEach(function (val, idx) {
                im.rectangle([val.x, val.y], [3, 3], [0, 255, 0], 3);
            });
            im.save(outputPath);
        });

        res.send({'code': '1', 'output': '/obstaclesimg/' + date + '.jpeg'});
    });
});

app.listen(config.port);