基于nodejs和SQLite的自助打印服务

ChainPray 发布于 2024-05-18 165 次阅读


起源

大一下有了大物实验,每周要打印实验报告,宿舍楼下的打印店拆了,然后就有很多人叫我帮忙打印。由于人太多了,我就用nodejs和elementUI写了个自助打印的程序,然后内网穿透到公网上,别人访问网站上传文件打印就可以了

附上github链接

moneypiaorui/PrintingServe: supply self-service printing service (github.com)

demo链接

效果

打印界面

用户需要注册登录,上传文件然后选择文件打印

管理界面

管理员用户可以登录后台查看所有用户,打印记录和所有用户上传的文件(筛选功能还没写),左侧的时间是时间戳,方便使用el-table的排序

文件预览

由于打印使用的pdf-to-printer包,采用libreoffice的api将docx,doc和图片转成pdf,但是libreoffice转pdf格式会出问题(毕竟开源项目),也没搞明白微软的pdf打印机api怎么使用,所以还是建议自己转成pdf再上传打印。

提供文件预览功能,防止docx和doc转pdf格式不符合预期

代码结构

​编辑

  • components是一些nodejs的组件,包括用户认证的中间件,数据库载入,转pdf等
  • dist放的是vue-cli项目build后的代码,可以删掉,前端用VUE-cli覆写虽然完成了,但代码太丑暂时搁置优化
  • public是静态资源,前端路由文件
  • router存放路由处理的代码
  • uploads存放用户上传的文件
  • app.js程序主入口
  • pretectedRouter.js无用,本来打算做用户权限管理,但在用户中间件实现了
  • user.db是SQLite数据库文件,里面有三个表,详细看数components下的据库组件

项目部署

  1. 安装nodejs,最新版就行,并配置好环境变量,保证nodejs能在终端使用
  2. clone本项目,或者github下载压缩包解压
  3. 下载libreoffice并配置环境变量保证soffice可以在终端调用
  4. 项目目录打开终端,npm run test或者node app.js启动服务

ps:暂只在windows上部署成功,linux没试

如果不想配置libreoffice,修改如下:

将printRouter.js替换如下

const path = require('path');
const printer = require('pdf-to-printer');
const express = require('express');
const { PDFDocument } = require('pdf-lib');
const fs = require('fs').promises;
const printRouter = express.Router();
const authMiddleware = require('../components/authMiddleware');
const wordToPdf = require('../components/officeToPdf');
const db = require('../components/database');

// 处理打印操作的路由
printRouter.post('/', authMiddleware, async (req, res) => {
    const { filename, timestamp } = req.body;
    const completeFilename = `${timestamp}-${filename}`;
    // 执行打印操作的逻辑
    if (filename) {
        let filePath = path.join(__dirname, '../uploads', completeFilename);
        const ext = path.extname(filename);

        printer
            .print(filePath, {
                printDialog: 0,
                monochrome: 1,//黑白打印
                copies: 1
            })
            .then(async () => {
                console.log(`文件${completeFilename}已打印`);
                if (path.extname(filePath) == ".pdf") {
                    await getPageCount(filePath)
                        .then(pageCount => {
                            res.status(200).json({ message: '文件已发送到打印机进行打印', pages: pageCount });
                            db.run(`INSERT INTO printLogs (filename, username, printTimestamp,pages) VALUES (?, ?, ?, ?)`, [filename, req.username, Date.now(), pageCount], function (err) {
                                if (err) {

                                } else {

                                }
                            })

                        })
                        .catch(err => {
                            res.status(200).json({ message: '文件已发送到打印机进行打印', err: "获取pdf页数时出错", pages: 0 });
                            console.error('获取 PDF 文件页数时出错:', err);
                        });
                } else {
                    res.status(200).json({ message: '文件已发送到打印机进行打印', err: "不是PDF", pages: 1 });
                }
            })
            .catch((err) => {
                console.error('打印失败:', err);
                res.status(500).json({ message: '打印失败' });
            });
    } else {
        res.status(400).send('未选择文件');
    }
});

async function getPageCount(pdfPath) {
    try {
        // 读取 PDF 文件
        const pdfBytes = await fs.readFile(pdfPath);
        // 打开 PDF 文档
        const pdfDoc = await PDFDocument.load(pdfBytes);
        // 获取 PDF 文件的页数
        const pageCount = pdfDoc.getPageCount();
        return pageCount;
    } catch (error) {
        console.error('读取 PDF 文件时出错:', error);
        return 0;
    }
}


module.exports = printRouter;

然后在public/index.html中100行左右找到fileType数组,删除doc和docx即可禁止用户上传该类文件 ​

深圳大学腾讯创新俱乐部的一名TICer,目前致力于成为全栈工程师
最后更新于 2024-06-15