????在線編輯office文檔(word、excel、powerpoint等)的實現(xiàn)方式可以歸集為兩種:
????1、純前端的在線編輯,如點聚WebOffice、金格WebOffice、PageOffice、ntko office、DSOFramer等,一般基于ActiveX控件實現(xiàn),在各個瀏覽器逐步嚴(yán)控甚至禁止各類客戶端控件的大背景下,這些解決方案的應(yīng)用空間將會越來越小。當(dāng)然近年也出現(xiàn)了一些基于云端office的解決方案,例如暢寫office,前端不再依賴ActiveX控件,但云端服務(wù)器上的留痕始終讓人不放心。
????2、后端創(chuàng)建、前端預(yù)覽方式,如果要修改后臺創(chuàng)建的office文檔,則可以采用前端下載后修改,然后在上傳更新后臺文檔;當(dāng)然也可以有限集成純前端在線編輯功能,以對后臺創(chuàng)建的文檔進行編輯,雖然筆者不建議這么做,畢竟前端的在線編輯功能再強大,也沒有在自己電腦上用office進行編輯來的方便、靈活。
????本文主要介紹第二種方式,即“后端創(chuàng)建、前端預(yù)覽”的相關(guān)實現(xiàn)方式,和注意事項。包括如下內(nèi)容:
1、 開發(fā)環(huán)境
2、 后端創(chuàng)建方式一:新創(chuàng)建文檔
3、 后端創(chuàng)建方式二:基于模板創(chuàng)建文檔
4、 前端預(yù)覽
5、 幾個坑
一、 開發(fā)環(huán)境
????前端,vuejs,UI是quasar
????后端,nodejs
二、 后端創(chuàng)建方式一:新創(chuàng)建文檔
????基于officegen(https://github.com/Ziv-Barber/officegen)實現(xiàn)office(word、excel、powerpoint)文檔的創(chuàng)建。
? ?安裝組件: npm i officegen --save-dev
? ??實現(xiàn)代碼:
import fs from 'fs'
import path from 'path'
import officegen from 'officegen'
module.exports = {
????createpgbg:async (rwbh) => {
????//創(chuàng)建word對象
????let docx = officegen('docx') ;
????docx.on('finalize', function(written){
????????console.log( '成功創(chuàng)建了word文件.' ) }) // Officegen calling this function to report errors:
????docx.on('error', function(err) { console.log(err) })
????let pObj = docx.createP() ;
????pObj.addText('Simple')
????pObj.addText(' with color', { color: '000088' })
????pObj.addText(' and back color.', { color: '00ffff', back: '000088' })
????pObj = docx.createP() pObj.addText('Since ')
????pObj.addText('officegen 0.2.12', { back: '00ffff', shdType: 'pct12', shdColor: 'ff0000' }) // Use pattern in the background.
????pObj.addText(' you can do ')
????pObj.addText('more cool ', { highlight: true }) // Highlight!
????pObj.addText('stuff!', { highlight: 'darkGreen' }) // Different highlight color.
????pObj = docx.createP()
????pObj.addText('Even add ')
????pObj.addText('external link', { link: 'https://github.com' })
????pObj.addText('!') pObj = docx.createP()
????pObj.addText('Bold + underline', { bold: true, underline: true })
????pObj = docx.createP({ align: 'center' })
????pObj.addText('Center this text', { border: 'dotted', borderSize: 12, borderColor: '88CCFF' })
????pObj = docx.createP()
????pObj.options.align = 'right'
????pObj.addText('Align this text to the right.')
????pObj = docx.createP()
????pObj.addText('Those two lines are in the same paragraph,')
????pObj.addLineBreak()
????pObj.addText('but they are separated by a line break.')
????docx.putPageBreak()
????pObj = docx.createP()
????pObj.addText('Fonts face only.', { font_face: 'Arial' })
????pObj.addText(' Fonts face and size.', { font_face: 'Arial', font_size: 40 })
????docx.putPageBreak()
????pObj = docx.createP()
????pObj.addImage('assets/uploads/1.jpg')
????let out = fs.createWriteStream('example1.docx')
????out.on('error', function(err) { console.log(err) })
????docx.generate(out)
}
}

三、后端創(chuàng)建方式二:基于模板創(chuàng)建文檔
????基于docxtemplater組件(https://github.com/open-xml-templating/docxtemplater)實現(xiàn)基于模板的office(word、excel、powerpoint)文檔創(chuàng)建。
????安裝組件:npm i docxtemplater?--save-dev
? ? ? ? ? ? ? ? ? ? ? npm i jszip@2.0.0 --save-dev
? ? ? ? ? ? ? ? ? ? ? ?npm i docxtemplater-image-module-free--save-dev
????實現(xiàn)代碼:
import fs from 'fs'
import path from 'path'
import jszip from 'jszip'
import docxtemplater from 'docxtemplater'
import docximagetemplater from'docxtemplater-image-module-free'
module.exports = {
? createpgbg:async(rwbh) => {
??? //打開word模板文件
??? let content =fs.readFileSync(path.resolve(__dirname,'../../assets/report/pgbgV2019.docx'),'binary');
??? let zip = newjszip(content) ;
??? let doc = newdocxtemplater() ;
??? let opts = {
????? centered:false,
?????fileType:'docx',
????? getImage:function(tagValue, tagName) {
??????? returnfs.readFileSync(path.join(__dirname, '../../assets/uploads/' + tagValue));
????? },
????? getSize:function(img, tagValue, tagName) {
??????? if(tagName== "a57"){
????????? return[350, 370];
??????? }
??????? elseif(tagName == "a58"){
????????? return[240, 180];
??????? }
??????? elseif(tagName == "a59"){
????????? return[240, 160];
??????? }
??????? else{
????????? return[100, 100];
??????? };
????? }
??? };
???doc.attachModule(new docximagetemplater(opts)) ;
???doc.loadZip(zip) ;
??? //裝載數(shù)據(jù)
??? doc.setData({
????? a53:'八一路',
????? a54:'蘇州東路',
????? a55:'康平路',
????? a56:'小區(qū)內(nèi)水、電、氣、有線電視等基礎(chǔ)設(shè)施配套齊全,附近有學(xué)校、超市、菜場,商業(yè)、服務(wù)網(wǎng)點齊全,附近有公交站臺,生活較方便',
?????a57:'img1.jpg',
?????a58:'img2.jpg',
?????a59:'img3.jpg'
??? }) ;
??? try{
????? //替換數(shù)據(jù)
????? doc.render();
???}catch(error){};
??? //保存文件
??? let buf =doc.getZip().generate({type:'nodebuffer'}) ;
???fs.writeFileSync(path.resolve(__dirname,'../../assets/report/pgbg.docx'),buf);
??? let filename ='2020-pgbg.docx' ;
??? return{fileurl:filename} ;
? }
}


四、前端預(yù)覽
前端預(yù)覽的選擇挺多,其中有三種方式經(jīng)過筆者實際使用,感覺挺不錯:
方式1:<iframe
src='https://view.officeapps.live.com/op/view.aspx?src=你需要展示的word文檔' />
方式2:<iframe src='http://ow365.cn/?i=1&furl=你需要展示的word文檔' />
方式3:<iframe src='http://www.xdocin.com/xdoc?_func=to&_format=html&_cache=1&_xdoc=你需要展示的word文檔' />

?????以上三種方式都要求“你需要展示的word文檔”必須是互聯(lián)網(wǎng)上能夠訪問的地址和文件名,此外一些使用心得如下:
????1、方式1免費,的文檔地址必須用encodeURIComponent進行編碼、地址必須為域名(不能用ip地址)、端口貌似也只能為80,文件大小有限制(word、ppt文件小于10M,excel文件小于5M)。
????2、方式2有限免費,使用前必須前在網(wǎng)站上注冊(免費版單文件<5M、日訪問量<500次)。但“你需要展示的word文檔”支持IP地址訪問,而且地址不需要編碼。缺點就是每頁都有一個OW365的LOGO,但不影響效果。
????3、方式3免費,而且支持IP地址訪問、地址也不需要編碼、不需要注冊賬號。缺點就是,顯示的時候?qū)嶋H上是自動轉(zhuǎn)換成了HTML,所以沒有office里的那種顯示質(zhì)感。另外還有一個小缺點就是顯示的速度有些慢,不過只要耐心,還是可以接受的。
五、幾個坑
1、word模板中的圖形顯示模塊
????Docxtemplater其實包含了images模塊(即支持在word模板中插入圖片的功能),但這個模塊要收費,所以只能另找一個免費的images模塊和Docxtemplater配合使用,而網(wǎng)上大部分文檔都推薦使用open-docxtemplater-image-module,而且還提供了詳細(xì)的實現(xiàn)代碼,但經(jīng)過筆者實際測試,不但顯示不了圖片,甚至還影響到Docxtemplater對文字模板的替換。
????而用docxtemplater-image-module-free組件,就可以正常配合Docxtemplater顯示出圖片,以上代碼就是經(jīng)過運行驗證的。
2、壓縮解壓組件
????在word模板創(chuàng)建中需要用到壓縮和解壓組件,Docxtemplater只支持2.0版本的jszip,如果安裝的是3.0+版本的jszip則在運行時就會報錯,而且不能正確創(chuàng)建出word文檔。這就是以上示例中強制安裝2.0版jszip的原因。
????但如果使用pizzip,就沒有版本的限制,可以正常使用。