nodeJS如何将docx转换为pdf

摘要:

本篇文件介绍在NodeJs中如何将Docx转换为PDF,通过使用libreoffice-convert和libreoffice进行转换。Docx文件在前端不太好分页,在前端需要控制用户的进度时使用PDF,已达到监听用户阅读进度及控制用户阅读速度的问题。

前言:最近做项目时,需要用户端展示docx文档,并监听用户当前阅读到第几页,控制用户在每页必须停留指定秒数,在使用了docx-to-pdfdocx-previewmammoth等库后,发现效果都不太理想,特别是分页方面。

上面提到的几个库的问题:

  1. docx-preview:分页有问题,需要docx文档中有分页符才行。
  2. mammoth:复杂样式丢失,这是此库故意而为,旨在让开发者使用css控制样式,但因我的需求里的文档文件内容不确定,就不太适用了。(用于导出docx的内容文字比较好用)
  3. docx-to-pdf:一个整合怪,使用mammothhtml-pdf实现,继承了mammoth的问题,没有复杂样式。

基于以上问题,最终选择了在服务端将docx转换为pdf,前端通过pdf.js进行控制即可。

安装依赖

bash 复制代码
npm i libreoffice-convert

安装libreoffice

libreoffice-convert库依赖libreoffice,需要先下载安装,地址:https://zh-cn.libreoffice.org/download/libreoffice/

编写代码

  1. 在有libreoffice环境变量的情况下,直接调用方法即可
js 复制代码
libre.convert('test.docx', '.pdf', undefined, (err, done) => {
  if (err) {
    return console.log('转换失败!', err)
  }
  fs.writeFileSync('test.pdf', done)
  console.log('转换成功!')
})
  1. 在没有libreoffice环境变量的情况下,特别时docker中,需要先安装程序,然后指定程序路径。
js 复制代码
libre.convertWithOptions('test.docx', '.pdf', undefined, {
  // 指定libreoffice程序的路径,这里使用window事例,其他系统同理
  sofficeBinaryPaths: ['C:/Program Files/LibreOffice/program/soffice.exe'],
}, (err, done) => {
  if (err) {
    return console.log('转换失败!', err)
  }
  fs.writeFileSync('test.pdf', done)
  console.log('转换成功!')
})

完整代码实现

对指定目录下的docx文件进行转换,并将转换后的文件存放在指定目录。

js 复制代码
const libre = require('libreoffice-convert')
const path = require('path')
const fs = require('fs')

// 存放docx的目录
const docxDirPath = path.join(__dirname, 'docx')
// 存放pdf的目录
const pdfDirPath = path.join(__dirname, 'pdf')

start()

// 启动
async function start() {
  const files = readDocxFile()
  // 队列执行,最大并行10个
  const results = await executeAsyncFunctionInQueue(ConvertDocToPdf, files, 10)
  console.log('转换完成', results)
}

// 获取目录下的所有docx文件
function readDocxFile() {
  const files = fs.readdirSync(docxDirPath)
  return files.filter((file) => file.endsWith('.docx'))
}

// 转换函数
function ConvertDocToPdf(file, index) {
  console.log(`开始转换第${index + 1}个文件: ${file}`)
  return new Promise((resolve, reject) => {
    const inputPath = path.join(docxDirPath, file)
    const outputPath = path.join(pdfDirPath, file.replace('.docx', '.pdf'))
    const docData = fs.readFileSync(inputPath)
    libre.convert(docData, '.pdf', undefined, (err, done) => {
      console.log(`第${index + 1}个文件: ${file}转换${err ? '失败' : '成功'}`)
      if (err) {
        return reject({
          name: file,
          error: err
        })
      }
      fs.writeFileSync(outputPath, done)
      resolve({
        name: file,
        error: null
      })
    })
  })
}

/**
 * 队列执行函数
 * @param {Function} asyncFunction 异步函数
 * @param {Array} params 数据数组
 * @param {Number} maxConcurrent 最大并行
 * @returns
 */
function executeAsyncFunctionInQueue(
  asyncFunction,
  params,
  maxConcurrent = 10
) {
  return new Promise((resolve) => {
    const total = params.length
    const results = []
    const executing = new Set()
    const executeNext = async () => {
      if (params.length === 0) {
        return
      }
      const currentIndex = total - params.length
      const param = params.shift()
      const promise = asyncFunction(param, currentIndex)
        .then((result) => {
          results.push(result)
        })
        .catch((error) => {
          results.push(error)
        })
        .finally(() => {
          executing.delete(promise)
          if (executing.size || params.length) {
            executeNext()
          } else {
            resolve(results)
          }
        })
      executing.add(promise)
    }
    while (executing.size < maxConcurrent && params.length > 0) {
      executeNext()
    }
  })
}

评论

0条评论

logo

暂无内容,去看看其他的吧~