• 需求:

数据可视化课程大作业需要制作一个网吧分析系统,需要用到node.js来在后台处理庞大的网吧数据和上网人员信息

  • 思路:

由于数据多达200W+,所以先在服务端将数据读取处理好,然后可以根据前端具体的需求来接受请求,返回相应的数据,比直接在前端使用mock.js处理数据效率更高

  • 步骤:
  1. 先使用fs模块加载原csv文件
    const hubReader = () => {
        return new Promise((resolve, reject) => {
            fs.readFile("./hub_data.csv", "utf8", (err, res2) => {
                if (!err) {
                    originData.hubdata = convertData(res2);
                    resolve("网吧信息读取完成");
                }
            });
        });
    };

    由于需要读取多个csv文件,所以可以使用promise将其包裹,等待读取文件完成然后resolve该promise,最后可以在主函数中调用promise.all()来确保所有数据都读取完成在进行端口监听,创建server等。

    const main = () => {
        Promise.all([
            hubReader(),
            personReader()
        ]).then((msg) => {
            console.log(msg);
            createHttpServer();
        });
    };
  2. 数据处理,将读取出来的string强行转换成我们需要的object对象,并将csv属性映射进去
    const convertData = (originString) => {
        originString = originString.replace(/"/g, "");
        let result = [];
        let originArray = originString.split("\n");
        let keys = originArray.shift().trim().split(",");
        originArray.forEach((current) => {
            current = current.split(",");
            let _ = {};
            for (let i in current) {
                if (current.hasOwnProperty(i) && keys[i] !== "") {
                    _[keys[i].toLocaleLowerCase()] = current[i];
                }
            }
            result.push(_);
        });
        return result;
    };
  3. 当确保所有本地文件读取完成后,在promise.then中进行下一步操作——创建httpServer
    const createHttpServer = () => {
        http.createServer(function (req, res) {
            const method = req.method.toLocaleLowerCase();
            if (methodFunction.hasOwnProperty(method)) {
                return methodFunction[method](req, res);
            } else {
                res.end("请求方法错误");
            }
        }).listen(8090);
    };

    判断请求方法是否在methodFunction这个事先定义好的对象中存在,并调用对应的方法,反之抛错

    const methodFunction = {
        "get": (req, res) => {
            const reqObject = url.parse(req.url, true);
            const reqPath = reqObject.path.replace(/\//, "");
            const response = originData[reqPath.toLocaleLowerCase()];
            const isIntegrity = originData.hasOwnProperty(reqPath);
            res.writeHead(isIntegrity ? 200 : 404, {'Content-Type': 'text/plain; charset=utf-8'});
            res.end(isIntegrity ? JSON.stringify(response) : "访问路径错误");
        },
        "post": (req, res) => {
            const reqObject = url.parse(req.url, true);
            const reqPath = reqObject.path.replace(/\//, "");
            let post = '';
            // 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
            req.on('data', function (chunk) {
                post += chunk;
            });
            // 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
            req.on('end', function () {
                const postBody = JSON.parse(post);
                // console.log(postBody);post请求参数
                const filter = (current) => {
                    let _ = true;
                    for (let _i in postBody) {
                        if (postBody.hasOwnProperty(_i)) {
                            _ = current[_i.toLocaleLowerCase()] == postBody[_i];
                        }
                    }
                    return _;
                };
                const response = originData[reqPath].filter((current) => {
                    return filter(current);
                });
                res.end(JSON.stringify(response));
            });
        }
    };

    在处理请求类型时需要注意,post请求需要多步拼接完成

  • 细节:

对于一般的get请求,我们只需要通过URL模块自带的url.parse()方法

const reqObject = url.parse(req.url, true);

来将url后面拼接的字符串提取成一个对象

对于post请求

req.on('data', function (chunk) {
    post += chunk;
});
// 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
req.on('end', function () {
});

需要不断拼接,在end阶段,则接受完所有数据此时可以进行其他操作

  • 数据查询功能:

由于没有设计到数据库,我们不能使用sql语句查询我们需要的数据所以我们可以使用Array.filter()实现类似的查询(模糊查询)

const postBody = JSON.parse(post);
const filter = (current) => {
    let _ = true;
    for (let _i in postBody) {
        if (postBody.hasOwnProperty(_i)) {
            _ = current[_i.toLocaleLowerCase()] == postBody[_i];
        }
    }
    return _;
};
const response = originData[reqPath].filter((current) => {
    return filter(current);
});

postBody是post请求的完整参数,我们遍历原数据的对应接口key值,return 一个函数,这个filter函数中,我们遍历post参数中的所有属性,并针对每个属性去原数据的每一项中查询,如果所有属性读符合特征,那么返回true,反之false,这样子就可以提取出我们需要查询的信息。

post请求参数

返回的查询结果

  • V8引擎貌似对node有最大内存限制
    /**
     * node --max-old-space-size=5000 index.js
     * 解除v8对node内存大小的限制
     * */

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注