最近工作中遇到一个md转docx的需求,故整理记录一下研究实现过程;
方案不用说,这种一般只能看有没有开源库实现了这个功能;
然后是前端做还是后端做的问题(都可以做);
前端实现:
经过一系列的搜索,最终锁定前端通常使用这两个库:
html‑to‑docx
html-to-docx 是一个 js 库,用于将 HTML 文档转换为 Microsoft Word 2007+、LibreOffice Writer、Google Docs、WPS Writer 等支持的 DOCX 格式。
它受到了html-docx-js项目的启发,但缓解了生成的文档与 Google Docs 和 libreOffice Writer 等不支持altchunks功能的文字处理器不兼容的问题。
目前它还不能直接在浏览器中使用,但已经针对 React 进行了测试。
它主要是配合 Nodejs 服务端使用
优点:功能相对丰富,支持复杂结构(表格、图片等)比一些老包强。
html‑docx‑js
既能在 现代浏览器 上使用,又能在 Nodejs 端使用
支持 UMD ,不支持 ESM
优点:轻量,基本需求可以满足。
缺点:对复杂样式、图片、表格等支持可能不如 html-to-docx 好。
前端实现步骤(html-docx-js):
项目情况:vue3+vite
模块化问题
-
如果你在 Vue 3 / Vite / React 等现代项目中直接用 ESM:
import htmlDocx from 'html-docx-js';会报错:
With statements cannot be used with the "esm" output format due to strict mode→ 因为源码使用了with,而现代打包器(Vite/Rollup/ESM)在严格模式下不允许with。 -
解决办法:
- 使用 UMD 版本,通过
<script>标签挂全局对象。 - 或自己用
vite.config.js配置optimizeDeps或build.commonjsOptions强制兼容 CommonJS。
- 使用 UMD 版本,通过
最终使用方法1 解决,方法2配置后没有解决报错。
项目中index.html 代码如下:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>md转docx</title>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<!-- 引入库 -->
<script src="https://unpkg.com/html-docx-js/dist/html-docx.js"></script>
</head>
<body>
<div id="app"></div>
<script type="module" src="src/main.ts"></script>
</body>
</html>
引入之后,全局会添加一个方法: window.htmlDocx
组件中具体使用代码如下:
if (window.htmlDocx) {
const html = `
// ....
`
const buffer = window.htmlDocx.asBlob(html);
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'markdown.docx';
a.click();
URL.revokeObjectURL(url); // 释放URL.createObjectURL内存
}
总结:
以上代码就实现了 html 转 docx的功能,最终实现效果一般,稍微复杂一点的结构可能就不理想; 例如有 嵌套的 ol - li ul - li 等效果就很不理想;正常的 p、h<1-5>标签等还是可以的;
- 如果需要转换的 html 比较简单这个方案还是可以的
后端实现:
现在前端都流行大前端,所以采用 nestjs + html-to-docx 接口实现此功能:
下载依赖: npm i html-to-docx -S
新建一个模块: nest g res fileOperation
file-operation.module.ts
import { Module } from '@nestjs/common';
import { FileOperationService } from './file-operation.service';
import { FileOperationController } from './file-operation.controller';
@Module({
controllers: [FileOperationController],
providers: [FileOperationService],
})
export class FileOperationModule {}
file-operation.service.ts
import { BadRequestException, Injectable } from '@nestjs/common';
// 引入方式这里注意一下,应为是 UMD,最好使用 require
const htmlToDocx = require('html-to-docx');
@Injectable()
export class FileOperationService {
// html 转 docx
async generateDocxFromHtml(html: string): Promise<Buffer> {
if (typeof html !== 'string' || html.trim().length === 0) {
throw new BadRequestException('参数 html 必须为非空字符串');
}
try {
const fileBuffer = await htmlToDocx(html, null, {
table: { row: { cantSplit: true } }, // 可选配置
footer: false,
pageNumber: false,
});
return fileBuffer;
} catch (error) {
console.log('error', error);
throw new BadRequestException('生成 Word 文档失败');
}
}
}
file-operation.controller.ts
import { Controller, Post, Body, Res, BadRequestException } from '@nestjs/common';
import { FileOperationService } from './file-operation.service';
import { FastifyReply } from 'fastify';
@Controller('file-operation')
export class FileOperationController {
constructor(private readonly fileOperationService: FileOperationService) {}
@Post('htmlToDocx')
async htmlToDocx(@Body() body: { html: string }, @Res() res: FastifyReply) {
if (!body || typeof body.html !== 'string') {
throw new BadRequestException('请求体缺少 html 字符串');
}
const buffer = await this.fileOperationService.generateDocxFromHtml(body.html);
res.header('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
res.header('Content-Disposition', 'attachment; filename="document.docx"');
return res.raw.end(buffer);
}
}
以上代码就实现了一个html 转 docx的接口;
总结:
经过测试 html-to-docx 实现的效果要比 html-docx-js 效果好很多;
其它:
网络上还有很多其它的实现方式:
-
pandoc
-
htmldocx(Python 库)
-
商业付费:AIEditor 自带富文本转word、pdf等
测试了效果比较好
-
商业/付费库:如 Aspose.Words for .NET