前言
本文使用前端框架React。
最近项目遇到一个需求,需要前端去导出word。百度了一圈后,找到了一个导出word的模版库,非常好用。
贴上地址:docxtemplater
借鉴了大佬的图片导出写法,下面贴上地址:
Word导出图片
功能点
1.导出指定排版的文章
2.导出带样式的文字
3.导出图片
4.导出表格
使用方法
首先讲一下在代码中怎么使用
首先导入第三方库:
import Docxtemplaterfrom 'docxtemplater'
import PizZipfrom 'pizzip'
import JSZipUtils from 'jszip-utils'
import { saveAs }from 'file-saver'
import ImageModule from "docxtemplater-image-module-free";
直接上代码(可直接复制):
/**
*函数传入参数说明:filepath:word保存的地址,
*wordData:word导出需要的数据,
*outPath:word导出文件名
* */
generateDocument = (filePath,wordData,outPath) => {
// 读取并获得模板文件的二进制内容,是docxtemplater提供的固定写法
JSZipUtils.getBinaryContent(filePath, function (error, content) {
// exportTemplate.docx是模板,React写在public里。我们在导出的时候,会根据此模板来导出对应的数据
//图片导出功能
let opts = {
centered:false,
fileType:"docx",
getSize: (img, tagValue, tagName)=>{
let width =100;
let height =100;
return [width,height];
},
getImage:(tagValue)=>{
/ /图片base64转字节数组
return base64DataURLToArrayBuffer(tagValue)
}
}
let imageModule = new ImageModule(opts);
// 创建一个PizZip实例,内容为模板的内容
let zip =new PizZip(content);
// 创建并加载docxtemplater实例对象
let doc =new Docxtemplater(zip,{modules:[imageModule]}).setData(wordData)
try{
doc.render();
}catch (error) {
function replaceErrors(key, value) {
if (valueinstanceof Error) {
return Object.getOwnPropertyNames(value).reduce(function(error, key) {
error[key] = value[key];
return error;
}, {});
}
return value;
}
if (error.properties && error.properties.errors instanceof Array) {
const errorMessages = error.properties.errors.map(function (error) {
return error.properties.explanation;
}).join("\n");
}
throw error;
}
// 生成一个代表docxtemplater对象的zip文件(不是一个真实的文件,而是在内存中的表示)
let out = doc.getZip().generate({
type : "blob",
mimeType : "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
});
//Output the document using Data-URI
saveAs(out, outPath);
})
}
//图片转base64字节码
function base64DataURLToArrayBuffer(dataURL) {
const base64Regex =/^data:image\/(png|jpg|svg|jpeg|svg\+xml);base64,/;
if (!base64Regex.test(dataURL)) {
return false;
}
const stringBase64 = dataURL.replace(base64Regex, "");
let binaryString;
if (typeof window !=="undefined") {
binaryString =window.atob(stringBase64);
}else {
binaryString =new Buffer(stringBase64, "base64").toString("binary");
}
const len = binaryString.length;
const bytes =new Uint8Array(len);
for (let i =0; i < len; i++) {
const ascii = binaryString.charCodeAt(i);
bytes[i] = ascii;
}
return bytes.buffer;
}
问题说明
JSZipUtils读取文件操作:
如果你的JSZipUtils读取不到word路径,你可以把文件放在public文件夹下。如果你的项目没有public文件夹。
1.重新创建项目
2.你可以把word放在src文件夹下任意文字
然后你需要在打包文件中指定word文件上传后读取的位置,不然打包发布后服务器读取不到文件
具体操作如下:
我的打包文件:
导入一个第三方库:const CopyWebpackPlugin = require(‘copy-webpack-plugin’);
在module.exports的plugins下加入
new CopyWebpackPlugin([
// {
// from: './src/MP_verify_Jf3BC19m68lvFLyl.txt',
// to: path.join(__dirname, '../dist/')
// },
{
from:'./src/word/1.docx',
to:path.join(__dirname, '../dist/')
},{
from:'./src/word/dz.docx',
to:path.join(__dirname, '../dist/')
},{
from:'./src/word/hx.docx',
to:path.join(__dirname, '../dist/')
},{
from:'./src/reports/x-text-1.html',
to:path.join(__dirname, '../dist/')
}
]),
如图所示
dist为build打包生成的文件
word模版讲解
代码加载word操作已经讲完了,下面轮到word模版和数据处理了。
首先wordData传递的数据是object键值对类型的数据。具体可看官方文档docxtemplater
1.排版
如果你需要插入固定格式的文字,例如下面这种
第一种模版写法为:
{-w:p products}{title}{/products}
数据格式:products:[{title:‘name’}]
第二种模版写法:
{#products}{.}{/products}
数据格式:products:[‘name’]
具体使用那种看你的数据格式
2.带格式的文本
如这种正数为红色,负数为绿色。
需要插入xml格式
模版非常简单,直接加上@就行了,如{@name1}
代码则需要插入docxXml格式
直接上代码
static indexInformationToXML = (color,content) =>{
return (
`<w:p>
<w:pPr>
<w:widowControl/>
<w:jc w:val="center"/>
<w:textAlignment w:val="center"/>
<w:rPr>
<w:rFonts w:ascii="楷体"
w:hAnsi="楷体"
w:eastAsia="楷体"
w:cs="楷体"/>
<w:color w:val="000000"/>
<w:sz w:val="20"/>
<w:szCs w:val="20"/>
</w:rPr>
</w:pPr>
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"
w:ascii="楷体"
w:hAnsi="楷体"
w:eastAsia="楷体"
w:cs="楷体"/>
<w:sz w:val="20"/>
<w:szCs w:val="20"/>
<w:color w:val="${color}"/>
</w:rPr>
<w:t>${content}</w:t>
</w:r>
</w:p>`
)
}
如果你还想插入更复杂的格式,则需要根据需要去查看生成好的docx xml模版的代码。
这个我们放到最后来说。
3.插入图片。
插入图片也很简单。模版写法:{%image}
不过官方的图片导出需要收费,所以我们用了一个免费的第三方库。具体写法前面已经给出,这里就不细讲。说一下导出图片需要注意的地方。
你传过去的数据中,图片必须是base64格式的。
关于图片转base64,下面直接上代码:
export function anyGetBase64(url,callback) {
let Img =new Image(),dataURL ='';
Img.src = url+'?t='+new Date().valueOf(); // 处理缓存,fix缓存bug,有缓存,浏览器会报错;
Img.setAttribute("crossOrigin", 'Anonymous')// 解决控制台跨域报错的问题
return new Promise((resolve, reject)=>{
Img.onload =function () {//要先确保图片完整获取到,这是个异步事件
let canvas =document.createElement("canvas"), //创建canvas元素
width = Img.width, //确保canvas的尺寸和图片一样
height = Img.height;
canvas.width = width;
canvas.height = height;
canvas.getContext("2d").drawImage(Img, 0, 0, width, height); //将图片绘制到canvas中
dataURL = canvas.toDataURL('image/png'); //转换图片为dataURL
// callback ? callback(dataURL) : null; //调用回调函数
resolve(dataURL)
}
})
}
图片转base64是异步操作,所以使用的时候逻辑处理最好写在finally后面。用法如下:
如果你访问的是服务器给的地址,有可能会报跨域的问题,前后端两边都要进行跨域配置。这里就不讲跨域操作了,请自行百度。
4.word导出表格
表格操作还是很常见的,这里讲讲动态导出多行表格。
首先还是先看模版写法,这里的写法和段落循环是一致的:
再来看看数据格式:
tfProductArray:[{productName:‘name’}],
具体就是这样。
下面是赠送内容
图文混排(打字有点累了,直接上代码)
我们还是先来看看模版
knowledges在最上面是为了去掉段落的空行,空行太多了不好看
因为图片的标签和段落的标签不一致,所以需要把他们分开写。录入数据的时候就不能全部录完段落,最后在录图片,这样的话图片就会在最后,所以需要在段落中依次录入
还是贴一下数据吧。数据就是这个样子。
最后说一下如何查看docx格式文件的xml代码
首先将你生成好的模版后缀名改成zip,然后通过解压工具解压,得到的就是一个文件夹
打开word,里面有个document.xml的文件,打开文件里面就是xml的代码了。
好了,关于React导出word的知识就讲完了。感谢大家耐心看完,如有问题可私信我,也可以在评论区发布。