利用node连接mongodb实现一个小型后端服务系统demo

http 请求

  • 实现get请求数据库数据;
  • 实现添加数据
  • 实现编辑数据
  • 实现删除数据
  • 实现导出txt文件、Excel文件
  • 实现查询数据库数据并利用导出为excel文件

node 版本 16.16.0

node 版本 18.16.0 会连接 MongoDB 数据库错误。

shell 复制代码
Connected to MongoDB failed MongoServerSelectionError: connect ECONNREFUSED ::1:27017
json 复制代码
{
  "name": "http-node-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "type": "module",
  "dependencies": {
    "body-parser": "^1.20.2",
    "mongodb": "^6.8.0",
    "querystring": "^0.2.1"
  }
}

项目安装 MongoDB 的 node 版本要跟启动时的 node 版本一致,否则会报错

js 复制代码
const timeoutError = new error_1.MongoServerSelectionError(
  `Server selection timed out after ${options.serverSelectionTimeoutMS} ms`,
  this.description
);

node 链接 MongoDB

js 复制代码
// MongoDB连接配置
const mongoConfig = {
  url: "mongodb://localhost:27017",
  dbName: "note", //数据库名称
};
js 复制代码
import { MongoClient } from "mongodb";
import { mongoConfig } from "./default.js";
const mongoDB = new MongoClient(mongoConfig.url);

let db;

async function mainFun() {
  // 创建MongoDB连接
  try {
    await mongoDB.connect();
    console.log("Connected to MongoDB successfully");
    db = mongoDB.db(mongoConfig.dbName);
  } catch (err) {
    console.log("Connected to MongoDB failed", err);
  }
}

export { mainFun, db };

接口返回的形势

  • 返回 404
js 复制代码
// 解析url
const urlObj = new URL(req.url, `http://${req.headers.host}`);

const params = {};
const apiKey = `${urlObj.pathname}&${req.method.toLocaleLowerCase()}`;
const item = ALL_PATH.find((v) => `/${v}` == apiKey);

// 找不到路由,返回404 Not Found
if (!item) {
  res
    .writeHead(404, {
      "Content-Type": "text/plain; charset=utf-8",
    })
    .end("Not Found");
  return;
}
  • 普通 JSON 字符串
js 复制代码
import { db } from "../mongoDB/index.js";
const getUser = async () => {
  try {
    // 查询数据集合user的所有数据并转为数组形式
    const collection = await db.collection("user").find({}).toArray();
    let total = 0;
    collection.estimatedDocumentCount(function (err, count) {
      if (err) throw err;
      total = count;
      console.log("Estimated total number of documents: ", count);
      // client.close();
    });

    return {
      code: 200,
      data: {
        list: collection,
        total: total || 0,
      },
    };
  } catch (err) {
    return {
      code: 500,
      data: {
        list: [],
        total: 0,
      },
    };
  }
};
  • 返回文件流
js 复制代码
import fs from "fs";
import path from "path";

const __dirname = path.resolve();

const downloadDocs = async () => {
  const promise = new Promise((resolve, reject) => {
    fs.readFile(__dirname + "/docs/新建文本文档.txt", (err, data) => {
      if (err) {
        console.error(err);
        reject("error");
        return;
      }
      resolve(data);
    });
  });

  try {
    const resData = await promise;
    return {
      code: 200,
      data: resData,
      contentType: "text/plain; charset=utf-8",
    };
  } catch (err) {
    return {
      code: 500,
      data: "",
      contentType: "text/plain; charset=utf-8",
    };
  }
};

const downloadNoteTemplate = async () => {
  try {
    const data = fs.readFileSync(__dirname + "/docs/Note模板.xlsx");
    return {
      code: 200,
      data: data,
      contentType: "application/vnd.ms-excel; charset=utf-8",
    };
  } catch (err) {
    return {
      code: 500,
      data: "",
      contentType: "application/vnd.ms-excel; charset=utf-8",
    };
  }
};
shell 复制代码
resData {
 code: 200,
  data: <Buffer 50 4b 03 04 0a 00 00 00 00 00 87 4e e2 40 00 00 00 00 00 00 00 00 00 00 00 00 09 00 00 00 64 6f 63 50 72 6f 70 73 2f 50 4b 03 04 14 00 00 00 08 00 87 ... 9951 more bytes>,
  contentType: 'text/plain; charset=utf-8'
}

响应头

当标头已使用 response.setHeader() 设置时,则它们将与任何传给 response.writeHead() 的标头合并,其中传给 response.writeHead() 的标头优先。

语法

js 复制代码
response.writeHead(statusCode[, statusMessage][, headers])
response.setHeader('Content-Type', 'text/html');
response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
js 复制代码
// Returns content-type = text/plain
const server = http.createServer((req, res) => {
  res.setHeader("Content-Type", "text/html");
  res.setHeader("X-Foo", "bar");
  res.writeHead(200, { "Content-Type": "text/plain" });
  res.end("ok");
});

返回 xlsx 文件,前端下载处理

后端接口

js 复制代码
const downloadNoteTemplate = async () => {
  try {
    const data = fs.readFileSync(__dirname + "/docs/Note模板.xlsx");
    return {
      code: 200,
      data: data,
      contentType: "application/vnd.ms-excel; charset=utf-8",
    };
  } catch (err) {
    return {
      code: 500,
      data: "",
      contentType: "application/vnd.ms-excel; charset=utf-8",
    };
  }
};

前端下载处理

js 复制代码
<Button onClick={() => {
  request('/download/noteTemplate',{
    method: 'get',
    {/*  */}
    responseType: "blob",
    responseEncoding: "utf8",
    options: {
      returnDirect: true, //returnDirect 直接返回接口所有信息
    },
  }).then((res) => {
    console.log('res',res);
    downloadContentFileFun('测试.xlsx',res)
  })
}}>下载</Button>
js 复制代码
export const downloadContentFileFun = (filename, text) => {
  // 下载Excel的文件, type: "application/vnd.ms-excel"需要与后端返回的content保持一致,否则会出现无法打开的情况
  let blob = new Blob([text], { type: "application/vnd.ms-excel" });
  const element = document.createElement("a");
  const href = URL.createObjectURL(blob);
  element.href = href;
  element.setAttribute("download", filename);
  element.style.display = "none";
  element.click();
  //调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了。
  URL.revokeObjectURL(href);
  element.remove();
};

返回 txt 文件,前端下载处理

后端接口

js 复制代码
const downloadDocs = async () => {
  const promise = new Promise((resolve, reject) => {
    fs.readFile(__dirname + "/docs/新建文本文档.txt", (err, data) => {
      if (err) {
        console.error(err);
        reject("error");
        return;
      }
      resolve(data);
    });
  });

  try {
    const resData = await promise;
    return {
      code: 200,
      data: resData,
      contentType: "text/plain; charset=utf-8",
    };
  } catch (err) {
    return {
      code: 500,
      data: "",
      contentType: "text/plain; charset=utf-8",
    };
  }
};

前端下载处理

js 复制代码
<Button
  onClick={() => {
    request("download/homeDoc", {
      method: "get",
      responseType: "blob",
      responseEncoding: "utf8",
      options: {
        returnDirect: true, //returnDirect 直接返回接口所有信息
      },
    }).then((res) => {
      console.log("res", res);

      downloadContentFileFun("测试.txt", res);
    });
  }}
>
  下载
</Button>
js 复制代码
export const downloadContentFileFun = (filename, text) => {
  // 这里的类型type改成application/vnd.ms-excel,发现也是可以正常打开的
  let blob = new Blob([text], { type: "text/plain; charset=utf-8" });
  const element = document.createElement("a");
  const href = URL.createObjectURL(blob);
  element.href = href;
  element.setAttribute("download", filename);
  element.style.display = "none";
  element.click();
  //调用这个方法来让浏览器知道不用在内存中继续保留对这个文件的引用了。
  URL.revokeObjectURL(href);
  element.remove();
};

关于前端下载文件的一些方法:https://blog.csdn.net/weixin_40119412/article/details/126980329

node 利用 excelJS 导出文件

js 复制代码
//exportExcel\excel.js
// 导入
import ExcelJS from "exceljs";

export default () => {
  // 创建工作薄
  const workbook = new ExcelJS.Workbook();

  // 设置工作簿属性
  workbook.creator = "System";
  workbook.lastModifiedBy = "System";
  workbook.created = new Date(2024, 7, 3);
  workbook.modified = new Date();
  workbook.lastPrinted = new Date(2024, 7, 3);

  // 将工作簿日期设置为 1904 年日期系统
  workbook.properties.date1904 = true;

  // 在加载时强制工作簿计算属性
  workbook.calcProperties.fullCalcOnLoad = true;

  workbook.views = [
    {
      x: 0,
      y: 0,
      width: 1000,
      height: 2000,
      firstSheet: 0,
      activeTab: 1,
      visibility: "visible",
    },
  ];
  return workbook;
};
js 复制代码
//exportExcel\index.js
import workbookFun from "./excel.js";

export default async (data, columns, sheetName = "sheet1") => {
  const workbook = workbookFun();

  // 添加工作表

  const sheet = workbook.addWorksheet(sheetName);

  sheet.columns = columns;
  // 将数据添加到工作表中
  sheet.addRows(data);

  // 后端 node返回接口直接使用buffer流
  const bufferData = await workbook.xlsx.writeBuffer();

  // 浏览器可以利用Blob下载文件 ;node直接返回Blob会提示错误
  // const blob = new Blob([bufferData], {
  //   type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  // });

  return bufferData;
};

利用 exceljs 库和 buffer.Blob 返回数据出错

原因 不支持返回 Blob 格式的数据,只支持字符串类型或 Buffer or Uint8Array

js 复制代码
(Use `node --trace-warnings ...` to show where the warning was created)
    ErrorCaptureStackTrace(err);
    ^

TypeError [ERR_INVALID_ARG_TYPE]: The "chunk" argument must be of type string or an instance of Buffer or Uint8Array. Received an instance of Blob

MongoDB 操作

查看集合

shell 复制代码
show collections;

返回信息

shell 复制代码
user_table
role_table

删除一个集合

shell 复制代码
db.user_table.drop();

创建一个集合

shell 复制代码
db.createCollection('template_table')

项目源码在gitee:https://gitee.com/yanhsama/node-http-demo

window安装mongodb可以查看文章:https://blog.csdn.net/weixin_40119412/article/details/130330496

mongodb的一些常用操作:https://blog.csdn.net/weixin_40119412/article/details/130566375

相关推荐
Lethehong5 小时前
Oracle 加个字段,目标库就报错?KFS 用“先拆后组“打通异构 DDL
数据库·oracle
Web极客码5 小时前
锁定真正的磁盘卡顿:我用 OpenCode 配合 eBPF 诊断 NVMe 队列深度延迟与 I/O 写放大
数据库·oracle
cspttty6 小时前
2027用户研究求职准备:应用统计学如何补齐研究设计与业务表达
大数据·数据库
roman_日积跬步-终至千里7 小时前
【数据Agent论文·查询与语义|05】Spider:为什么换一个数据库,模型就不会查了?
数据库·数据分析
猫咪很可爱7 小时前
Oracle RAC 启动失败排查:私网通信与内核参数优化实战
运维·数据库·oracle
XLYcmy8 小时前
《数字经济赋能城市低碳治理的路径研究检索报告》审阅与修改指导意见——基于完整检索报告体例的完整性诊断、学术规范核查与方法论纠偏 上
数据库·ai·chatgpt·文心一言·检索·知网·豆包
泡海椒8 小时前
JQuick-SQL 快速开始:注册内存表并执行查询
数据库·windows·sql
ss2738 小时前
PG实战笔记 | 09 PostgreSQL psql客户端基本操作
数据库·笔记·postgresql
电商API_180079052478 小时前
反向海淘多币种结算:汇率该在哪个时点被冻结
java·开发语言·数据库·跨境电商·1688 api
搞AI的金鱼9 小时前
skill agent workflow的区别
数据库