- 需求:
数据可视化课程大作业需要制作一个网吧分析系统,需要用到node.js来在后台处理庞大的网吧数据和上网人员信息
- 思路:
由于数据多达200W+,所以先在服务端将数据读取处理好,然后可以根据前端具体的需求来接受请求,返回相应的数据,比直接在前端使用mock.js处理数据效率更高
- 步骤:
- 先使用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(); }); };
- 数据处理,将读取出来的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; };
- 当确保所有本地文件读取完成后,在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内存大小的限制 * */