HTML轉PDF的純客戶(hù)端和純服務(wù)端實(shí)現方案
時(shí)間:2026-05-05 07:11:03這篇文章主要介紹了HTML轉PDF的純( ?▽?)端和純客戶(hù)端和純服務(wù)端實(shí)現方案,文中通過(guò)示例代碼介紹的客戶(hù)非常詳細,對大家的??純服學(xué)習或者工作具有一定的參考學(xué)??習價(jià)值,需要的實(shí)現朋友們下面隨著(zhù)小編來(lái)一起學(xué)習學(xué)習吧
需求
用戶(hù)填寫(xiě)表單,點(diǎn)擊保存之后,純端和可以直接下載pdf文檔??蛻?hù)
解決思路
服務(wù)端生成
思路
谷歌瀏覽器在17年自行開(kāi)發(fā)了Chrome Headless特性,純服并與之同時(shí)推出了 puppeteer,實(shí)現它可以被理解為是純端和無(wú)界面但┐(′д`)┌是可以完成服務(wù)器功??能特性的瀏覽器。
所以我們可以在服務(wù)端啟動(dòng)puppeteer瀏覽器,客戶(hù)打開(kāi)目(?????)標網(wǎng)址,純服使用chrome瀏覽器自帶的實(shí)現轉換功能進(jìn)行html到pdf的轉換。
服務(wù)端生(???)成核心代碼
輸入 cnpm i puppe(//ω//)teer -S 安裝依賴(lài)。
// html2pdf.j??s
const puppeteer = require('puppeteer');
(async function(){
// 啟動(dòng)服務(wù)
const brow(′ω`)ser = await puppeteer.launch();
// 打開(kāi)標簽頁(yè)
coヽ(′▽?zhuān)?ノnst page = await browser.newPage();
// 轉到該地址
await pa??ge.goto('https://koa.bootcss.com/#context');
// html頁(yè)面轉pdf并保存至path
await page.pdf({ path:"test.pd??f",format:'A4'})
// 關(guān)閉瀏覽器
await browser.close();
})();
然后控制臺輸入 node html2pdf.js 啟動(dòng)服務(wù)。
當然也可以module.export將模塊方法導出,根???據業(yè)務(wù)邏輯來(lái)。
缺點(diǎn)
無(wú)法保存表單(′Д` )動(dòng)態(tài)數據
由于是從服務(wù)端請求頁(yè)面,如果不在請求地址上??保存(cun)用戶(hù)輸??入,截出來(lái)的pdf將是頁(yè)面沒(méi)被填寫(xiě)的初始狀態(tài)。
換而言之,他只能進(jìn)行靜態(tài)頁(yè)面的轉換,因為我們的需求有大量ˉ\_(ツ)_/ˉ用戶(hù)輸入,因此pass。
客戶(hù)端生成核心代碼
思路
使??用html2canvas,輸入需要轉換的dom節點(diǎn),遍歷轉換成canv??as畫(huà)布
將canvas畫(huà)布轉成b??ase64圖片,使用jsPDF創(chuàng )建pdf文件,把圖片??插入進(jìn)pdf。
失真。
我們可以很明顯的發(fā)現,既然是類(lèi)似于對頁(yè)面截圖再將截圖插入pd??f,頁(yè)面的分辨率和(???)配置很可能影響輸出圖片的質(zhì)量。
同時(shí),因為是截圖,可能失去頁(yè)面鏈接等功能。
當canvas畫(huà)布大于pdf一頁(yè)大小時(shí),輸出就(╯‵□′)╯會(huì )出錯,這時(shí)我們需要判斷c( ?ヮ?)anvas畫(huà)布是??否超出??A4大小,如果超出,對canvas進(jìn)行分割,插入到不同的頁(yè)面。
這時(shí)候問(wèn)題又來(lái)??了,既然是分割圖片,那么很可能導(′?_?`)致圖片或者文(????)字從一??半就被截斷,因為我們無(wú)法分析canvas內部item的結構。
核心代碼
我們的需求??沒(méi)有圖片和鏈接,所以失真的問(wèn)題對我們影響不大??,同時(shí)我們的表單由多個(gè)重復等長(cháng)的it??em組成,并且這些item都非常短,不會(huì )超出一張A4紙(雖然這樣不嚴謹,如果需要,你可以獲(′;д;`)取DOM元素寬高,根據DOM元素高度裁剪)。
所以我打算直接根據i??tem切分canvas,每個(gè)item給一頁(yè)A4紙保存。
在開(kāi)始之前需要理解幾個(gè)核心方法:
html2canvas
// DOM是要轉換的DOM節點(diǎn)
html2canvas(DOM,{
backgroundColor:"??#ffffff",
width:width,
height:height,
scale:2,
allowTaint:true,
}).then((canv┐(′д`)┌as)=>(′_`);{
// canvas 是轉換成功后的畫(huà)布
})
js??PDF
// 創(chuàng )建實(shí)例
let pdf = new jsPDF('','pt','a4');
// 將圖片添加到pdf文件里
// 第一個(gè)參數是待插入的文件(base64)格式,第二個(gè)是文件格式
// 第三第四是圖片(pian)左上角的坐標,最后兩個(gè)是圖片插入后的寬高
pdf.addImage(image,'JPEG',10,10,height,width);
// 添加新的一頁(yè)
pd(°□°)f.adヽ(′▽?zhuān)?ノdPage()
// 保存pdf文件
pdf.save()
canvas
// canvas是待剪切的圖??片
// sx,sy是開(kāi)始裁剪的坐標
// swidth、sHeight是裁剪的寬高
// dx、dy是裁剪后圖像在canvas中插入的坐標
// sWidth,sHeight是裁剪后圖像在canvas中的寬高
cxt.drawImage(canvas,sx,sy,sWidth,sHeight,dx,dy,sWidth,sHeight);
/**
* @des(??ヮ?)?*:???cription: 表單轉pdf文件
* @return: pdf
*/
onSubmit(){
// 這是我要轉換的表單,里面有很多一樣的表格
let form = thi??s.$refs.form;
// 獲取元素的(de)寬高
let width = form.getBoundingClientRect().width;
let height = form.getBoundingClientRect().height;
html2canvas(form,{
backgroundColor:"#ffffff",
width:width,
height:height,
scal??e:2,
allowTaint:true,
}).thヾ(′ω`)?en((canvas)=>{
let pdf = new jsPDF('','pt','a4');
// 進(jìn)行圖片切割
let canvasList = this.splitCanvas(canvas,this.forms.length);
// 遍歷canvas列表,每??頁(yè)添加一張圖片
canvasList.forEach((item,index)=>{
// 轉換圖片格式為base64
le(°o°)t itemImage = item.toDataURL('image/jpeg',1.0)??;
// 預留10px邊距,A4紙的寬在72分辨率的顯示器上是595px
pdf.addImage(itemIma??ge,'JPEG',10,10,575.28,575.28/item.width*item.height);
//(′Д` ) 如果不是最后一頁(yè),則分頁(yè)
index == this.forms.length-1 ? '' : pdヽ(′▽?zhuān)?ノf.addPage();
})
// 文件保(′▽?zhuān)?存
let blob = pdf.output('blob');
pdf.sa??ve('test.pdf');
})
},
/**
* @des??cription: 對canvas進(jìn)行切割
* @param { number} num 切片數量
* @param { canvas} canvas
* @return { array} canvas列表
*/
splitCanvas(canvas,num){
let height = canvas.h??e( ?ω?)ight,width = canヽ(′▽?zhuān)?ノvas.width;
let chunkHeight = height/num;// 每個(gè)切片的高度?
let chunkList = [];// 存放結果canvas
for(let i=0; i<height ; i+=chunkHeight){
// 初始化裁剪矩形框位置
let sx = 0,sy = i,sWidth = width,sHeight = chunkHeig( ?ω?)ht,dx = 0, dy = 0;
// 創(chuàng )建一個(gè)canvas節點(diǎn)
let canvasItem =document.createElement("canvas");
// 初始化畫(huà)布大小
canvasItem.height = chunkHeight;
canvasItem.wi??dth = width;
let cxt = canva( ?° ?? ?°)sItem.getContext("2d");
// 將裁剪的圖片放到新的canvas節點(diǎn)
cxt.drawImage(canvas,sx,sy,sWidth,sHeight,dx,dy,sWidth( ?ヮ?),sHeight);
chunkList.push(canvasItem);
}
return chunkList;
},
到此這篇關(guān)于H(′?`)TML轉PDF的純客戶(hù)端和純服務(wù)端實(shí)現方案的文章(′?`)就介紹到這了,更多相關(guān)HTML轉PDF內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!
來(lái)源:腳本之家
鏈接:h?ttps://www.jb51.net/(′?ω?`)web/72(′?_?`)2457.htm??l
客服電話(huà)17790068725
Copyright ? 2012-2018 天津九安特機電工程有限公司 版權所有 備案號:
客服電話(huà)13332133228