当前位置: 代码迷 >> 综合 >> react实现下载(download属性,jspdf,html-docx-js,wkhtmltopdf)
  详细解决方案

react实现下载(download属性,jspdf,html-docx-js,wkhtmltopdf)

热度:101   发布时间:2023-11-26 01:08:11.0

一、最简单的方法:利用download属性

download属性内填值表示指定下载的文件名 

<a href={Pig} download="details.jpeg">下载</a>

<a href={Pig} download>下载</a>

二、jspdf(比较简单,但是不能转化网页内的图片)

如果想在下载完成后,执行setState,那么先在dowload事件开头定义:const self = this;然后在pdf.save语句后执行self.setState({  });

npm install jspdf --save
npm install react-dom --save
import React, { Component } from "react";
import "./App.css"
import { renderToString } from "react-dom/server";
import jsPDF from "jspdf";
class Prints extends Component {render() {return (<div><p style={
   {fontSize:18,color:"purple"}}>Convert webpage to pdf format and download</p></div>);}
}
const print = () => {const string = renderToString(<Prints />);const pdf = new jsPDF("p", "mm", "a4",'JPEG', 0, 0, 100, 50);pdf.fromHTML(string);pdf.save("pdf");
};
class App extends Component {render() {return (<div><Prints /><button onClick={print}>print</button></div>);}
}
export default App;

三、html2canvas + jsPDF单页

import React, { Component } from "react";
import "./App.css"
import jsPDF from "jspdf";
import html2canvas from "html2canvas";
import Pig from "./pig.jpg"
class App extends Component {download() {html2canvas(document.body).then(function(canvas) {//返回图片dataURL,参数:图片格式和清晰度(0-1)var pageData = canvas.toDataURL('image/jpeg', 1.0);//方向默认竖直,尺寸ponits,格式a4[595.28,841.89]var pdf = new jsPDF('', 'pt', 'a4');//addImage后两个参数控制添加图片的尺寸,此处将页面高度按照a4纸宽高比列进行压缩pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28/canvas.width * canvas.height );pdf.save('medical-details.pdf');});}render() {return (<div id="canvas"><img src={Pig} alt ="img" style={
   {width:400,height:400}} /><p style={
   {fontSize:28,color:"purple"}}>img</p><button onClick={() => this.download()}>Download Report</button></div>);}
}
export default App;

四、html2canvas + jsPDF分页

问题:

  • 图片跨域:

html2canvas的配置项中配置 allowTaint:true 或 useCORS:true(二者不可共同使用)
img标签增加 crossOrigin='anonymous'
图片服务器配置Access-Control-Allow-Origin 或使用代理

  • svg不能显示
import React, { Component } from "react";
import "./App.css"
import jsPDF from "jspdf";
import html2canvas from "html2canvas";
import Pig from "./pig.jpg"
import canvg from "canvg"
import $ from 'jquery'
class App extends Component {download() {if (typeof html2canvas !== 'undefined') {//以下是对svg的处理var nodesToRecover = [];var nodesToRemove = [];var svgElem = $("#canvas").find('svg');//divReport为需要截取成图片的dom的idsvgElem.each(function (index, node) {var parentNode = node.parentNode;var svg = node.outerHTML.trim();var canvas = document.createElement('canvas');canvg(canvas, svg);if (node.style.position) {canvas.style.position += node.style.position;canvas.style.left += node.style.left;canvas.style.top += node.style.top;}nodesToRecover.push({parent: parentNode,child: node});parentNode.removeChild(node);nodesToRemove.push({parent: parentNode,child: canvas});parentNode.appendChild(canvas);});html2canvas((document.body),{useCORS: true,allowTaint: false,scale: 2}).then(function (canvas) {var pageData = canvas.toDataURL('image/jpeg', 1.0);//方向默认竖直,尺寸ponits,格式a4[595.28,841.89]var pdf = new jsPDF('', 'pt', 'a4');//addImage后两个参数控制添加图片的尺寸,此处将页面高度按照a4纸宽高比列进行压缩pdf.addImage(pageData, 'JPEG', 0, 0, 595.28, 592.28 / canvas.width * canvas.height);pdf.save('medical-details.pdf');});}}render() {return (<div id="canvas"><button onClick={() => this.download()}>Download Report</button><svgwidth="100%"height="100%"id="svg_style"preserveAspectRatio="xMidYMid meet"viewBox="0 0 380 360"><image xlinkHref={Pig} x="0" width="50%" height="50%" /></svg></div>);}
}
export default App;

具体代码

html2canvas(document.body, {useCORS: true,// allowTaint: true,logging: false,scale: 2}).then(function(canvas) {var contentWidth = canvas.width;var contentHeight = canvas.height;//一页pdf显示html页面生成的canvas高度;var pageHeight = (contentWidth / 595.28) * 841.89;//未生成pdf的html页面高度var leftHeight = contentHeight;//页面偏移var position = 0;//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高var imgWidth = 595.28;var imgHeight = (592.28 / contentWidth) * contentHeight;var pageData = canvas.toDataURL("image/jpeg", 1.0);var pdf = new jsPDF("", "pt", "a4");//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)//当内容未超过pdf一页显示的范围,无需分页if (leftHeight < pageHeight) {pdf.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);} else {while (leftHeight > 0) {pdf.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);leftHeight -= pageHeight;position -= 841.89;//避免添加空白页if (leftHeight > 0) {pdf.addPage();}}}pdf.save("medical-details.pdf");
});
  • 分页的另外一种写法
download() {html2canvas(document.getElementById("userReportTable"), {useCORS: true,// 是否允许网页中img元素跨域,这个设置需要img元素支持及服务器支持allowTaint: true,// 支持跨域scale: 2// 这个影响生成图片的清晰度}).then(function(canvas) {var pdf = new jsPDF("p", "mm", "a4"); //A4纸,纵向var ctx = canvas.getContext("2d"),a4w = 190,a4h = 277, //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277imgHeight = Math.floor((a4h * canvas.width) / a4w), //按A4显示比例换算一页图像的像素高度renderedHeight = 0;while (renderedHeight < canvas.height) {var page = document.createElement("canvas");page.width = canvas.width;page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能内容不足一页//用getImageData剪裁指定区域,并画到前面创建的canvas对象中page.getContext("2d").putImageData(ctx.getImageData(0,renderedHeight,canvas.width,Math.min(imgHeight, canvas.height - renderedHeight)),0,0);pdf.addImage(page.toDataURL("image/jpeg", 1.0),"JPEG",10,10,a4w,Math.min(a4h, (a4w * page.height) / page.width)); //添加图像到页面,保留10mm边距renderedHeight += imgHeight;if (renderedHeight < canvas.height) {pdf.addPage();} //如果后面还有内容,添加一个空页}pdf.save("content.pdf");});
  • 设置左右边距,如下图所示,设置左右边距20

  • 解决多页截断问题

有没有大神有解决办法呀。求一个解决办法

  • 完整代码,简单分页
import React, { Component } from "react";
import { Button, Icon, Table } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import jsPDF from "jspdf";
import html2canvas from "html2canvas";
class App extends Component {download() {html2canvas(document.getElementById("detail_report_dowload_container"), {useCORS: true,// allowTaint: true,logging: false,scale: 2}).then(function (canvas) {var contentWidth = canvas.width;var contentHeight = canvas.height;//一页pdf显示html页面生成的canvas高度;var pageHeight = (contentWidth / 592.28) * 841.89;//未生成pdf的html页面高度var leftHeight = contentHeight;//页面偏移var position = 0;//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高var imgWidth = 555.28;var imgHeight = (555.28 / contentWidth) * contentHeight;var pageData = canvas.toDataURL("image/jpeg", 1.0);var pdf = new jsPDF("", "pt", "a4");//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)//当内容未超过pdf一页显示的范围,无需分页if (leftHeight < pageHeight) {pdf.addImage(pageData, "JPEG", 20, 0, imgWidth, imgHeight);} else {while (leftHeight > 0) {pdf.addImage(pageData, "JPEG", 20, position, imgWidth, imgHeight);leftHeight -= pageHeight;position -= 841.89;//避免添加空白页if (leftHeight > 0) {pdf.addPage();}}}pdf.save("detail-report.pdf");});}render() {return (<div className="docReport-container"><Button className="dowButton" basic onClick={() => this.download()}><Icon name="download" />Download Report</Button><div id="detail_report_dowload_container"><Table celled className="report_table details_report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th_first">app</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td_first">123</Table.Cell></Table.Row></Table.Body></Table><Table celled className="report_table details_report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th_first">study</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td_first">qwe</Table.Cell></Table.Row></Table.Body><Table.Header><Table.Row><Table.HeaderCell className="report_table_th_first">test</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td_first">123</Table.Cell></Table.Row></Table.Body></Table></div></div>)}
}
export default App;
  • 根据classname个数,下载成多个文件
import React, { Component } from "react";
import { Button, Icon, Table } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import jsPDF from "jspdf";
import "./App.css"
import html2canvas from "html2canvas";
class detailsReport extends Component {download() {// 要导出pdf的节点var elements = document.getElementsByClassName("report_table");for (var index = 0; index < elements.length; index++) {html2canvas(elements[index], {allowTaint: true, // 支持跨域useCORS: true, // 是否允许网页中img元素跨域,这个设置需要img元素支持及服务器支持scale: 2, // 这个影响生成图片的清晰度background: "#F5F5F5" //背景}).then(function (canvas) {var contentWidth = canvas.width;var contentHeight = canvas.height;//一页pdf显示html页面生成的canvas高度;var pageHeight = (contentWidth / 592.28) * 841.89;//未生成pdf的html页面高度var leftHeight = contentHeight;//页面偏移var position = 0;//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高var imgWidth = 555.28;var imgHeight = (555.28 / contentWidth) * contentHeight;var pageData = canvas.toDataURL("image/jpeg", 1.0);var pdf = new jsPDF("", "pt", "a4");//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)//当内容未超过pdf一页显示的范围,无需分页if (leftHeight < pageHeight) {pdf.addImage(pageData, "JPEG", 20, 0, imgWidth, imgHeight);} else {while (leftHeight > 0) {pdf.addImage(pageData, "JPEG", 20, position, imgWidth, imgHeight);leftHeight -= pageHeight;position -= 841.89;//避免添加空白页if (leftHeight > 0) {pdf.addPage();}}}pdf.save("detail-report.pdf");});};}render() {return (<div className="docReport-container"><Button className="dowButton" basic onClick={() => this.download()}><Icon name="download" />Download Report</Button><div id="detail_report_dowload_container"><Table celled className="report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th">test</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_th">ret</Table.Cell></Table.Row></Table.Body></Table><Table celled className="report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th">study</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td">0</Table.Cell></Table.Row></Table.Body><Table.Header><Table.Row><Table.HeaderCell className="report_table_th">row</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td">123</Table.Cell></Table.Row></Table.Body></Table></div></div>)}
}
export default detailsReport;
  • 根据classname个数,下载时分页
import React, { Component } from "react";
import { Button, Icon, Table } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import jsPDF from "jspdf";
import html2canvas from "html2canvas";
class App extends Component {constructor(props) {super(props);this.state = {pageLength: 0};}/*** 导出为pdf* @param total 需要导出的总页数*/download(total) {console.log("total",total)html2canvas(document.getElementById("detail_report_dowload_container"), {useCORS: true,logging: false}).then(function(canvas) {var pdf = new jsPDF("p", "mm", "a4"); //A4纸,纵向var ctx = canvas.getContext("2d"),a4w = 190,a4h = 277, //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277// imgHeight = Math.floor((a4h * canvas.width) / a4w), //按A4显示比例换算一页图像的像素高度imgHeight = canvas.height / total + 0.001, //按A4显示比例换算一页图像的像素高度(+1是为了防止画布大小正好等于图片大小时溢出导致crash)renderedHeight = 0;console.log("imgHeight: ", imgHeight);console.log("canvasHeight: ", canvas.height);pdf.page=1;while (renderedHeight <= canvas.height) {var page = document.createElement("canvas");page.width = canvas.width;// page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能内容不足一页page.height = canvas.height / total; //可能内容不足一页console.log("page.height: ", page.height);//用getImageData剪裁指定区域,并画到前面创建的canvas对象中page.getContext("2d").putImageData(ctx.getImageData(0,((renderedHeight / imgHeight) * canvas.height) / total,canvas.width,Math.min(imgHeight, canvas.height - renderedHeight)),0,0);pdf.setFontSize(10);pdf.text(204,294, String(pdf.page)); //print number bottom rightpdf.page++ ;pdf.addImage(page.toDataURL("image/jpeg", 1.0),"JPEG",10,10,a4w,Math.min(a4h, (a4w * page.height) / page.width)); //添加图像到页面,保留10mm边距renderedHeight += imgHeight;if (renderedHeight < canvas.height) {pdf.addPage(); //如果后面还有内容,添加一个空页// delete page;}}pdf.save("detail-report.pdf");});}componentDidMount(){const length = document.getElementsByClassName("report_table").lengththis.setState({pageLength:length})}render() {return (<div className="docReport-container"><Button className="dowButton" basic onClick={() => this.download(this.state.pageLength)}><Icon name="download" />Download Report</Button><div id="detail_report_dowload_container"><Table celled className="report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th">app</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td">123</Table.Cell></Table.Row></Table.Body></Table><Table celled className="report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th">study</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td">qwe</Table.Cell></Table.Row></Table.Body></Table></div></div>)}
}
export default App;
  • 限定某部分内容在前面几页
import React, { Component } from "react";
import { Button, Icon, Table } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import jsPDF from "jspdf";
import html2canvas from "html2canvas";
class App extends Component {constructor(props) {super(props);this.state = {pageLength: 0};}/*** 导出为pdf* @param total 需要导出的总页数*/download(total) {let reportHeight = 0;html2canvas(document.getElementById("userReportTable_report"), {useCORS: true,logging: false}).then(function(canvas) {reportHeight = canvas.height;});html2canvas(document.getElementById("userReportTable"), {useCORS: true,logging: false}).then(function(canvas) {var pdf = new jsPDF("p", "mm", "a4"); //A4纸,纵向var ctx = canvas.getContext("2d"),a4w = 190,a4h = 277, //A4大小,210mm x 297mm,四边各保留10mm的边距,显示区域190x277// imgHeight = Math.floor((a4h * canvas.width) / a4w), //按A4显示比例换算一页图像的像素高度imgHeight = (canvas.height - reportHeight) / total + 0.001, //按A4显示比例换算一页图像的像素高度(+1是为了防止画布大小正好等于图片大小时溢出导致crash)imgTableHeight = reportHeight + 0.001, //按A4显示比例换算一页图像的像素高度(+1是为了防止画布大小正好等于图片大小时溢出导致crash)renderedHeight = 0,renderTableHeight = 0;console.log("reportHeight", reportHeight);pdf.page = 1;while (renderTableHeight <= reportHeight) {// 这个-1时因为有时var page = document.createElement("canvas");page.width = canvas.width;page.height = Math.min(imgTableHeight, reportHeight - renderedHeight); // 可能内容不足一页// 用getImageData剪裁指定区域,并画到前面创建的canvas对象中page.getContext("2d").putImageData(ctx.getImageData(0,renderTableHeight,canvas.width,Math.min(imgTableHeight, reportHeight - renderTableHeight + 0.001)),0,0);pdf.setFontSize(10);pdf.text(204, 294, String(pdf.page)); //print number bottom rightpdf.page++;pdf.addImage(page.toDataURL("image/jpeg", 1.0),"JPEG",10,10,a4w,(a4w * page.height) / page.width); // 添加图像到页面// console.log(page.height, page.width, Math.min(a4h, a4w * page.height / page.width))renderTableHeight += imgTableHeight;if (renderTableHeight <= reportHeight) {pdf.addPage(); // 如果后面还有内容,添加一个空页}}pdf.addPage();while (renderedHeight <= canvas.height - reportHeight) {page = document.createElement("canvas");page.width = canvas.width;// page.height = Math.min(imgHeight, canvas.height - renderedHeight); //可能内容不足一页page.height = (canvas.height - reportHeight) / total; //可能内容不足一页console.log("page.height: ", page.height);//用getImageData剪裁指定区域,并画到前面创建的canvas对象中page.getContext("2d").putImageData(ctx.getImageData(0,(((renderedHeight + reportHeight) / imgHeight) *(canvas.height - reportHeight)) /total,canvas.width,Math.min(imgHeight, canvas.height - reportHeight - renderedHeight)),0,0);pdf.setFontSize(10);pdf.text(204, 294, String(pdf.page)); //print number bottom rightpdf.page++;pdf.addImage(page.toDataURL("image/jpeg", 1.0),"JPEG",10,10,a4w,Math.min(a4h, (a4w * page.height) / page.width)); //添加图像到页面,保留10mm边距renderedHeight += imgHeight;if (renderedHeight < canvas.height - reportHeight) pdf.addPage(); //如果后面还有内容,添加一个空页// delete page;}pdf.save("test.pdf");});}componentDidMount(){const length = document.getElementsByClassName("report_table").lengththis.setState({pageLength:length})}render() {return (<div className="docReport-container"><Button className="dowButton" basic onClick={() => this.download(this.state.pageLength)}><Icon name="download" />Download Report</Button><div id="userReportTable"><div id="userReportTable_report"><div className="user_report_title"><strong>Summary</strong></div></div><div id="detail_report_dowload_container"><Table celled className="report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th">app</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td">123</Table.Cell></Table.Row></Table.Body></Table><Table celled className="report_table"><Table.Header><Table.Row><Table.HeaderCell className="report_table_th">study</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="report_table_td">qwe</Table.Cell></Table.Row></Table.Body></Table></div></div></div>)}
}
export default App;
  • 目前还在考虑一个比较实用的方法,等待demo上传

https://www.zhangxinxu.com/wordpress/2017/07/js-text-string-download-as-html-json-file/

1.将当前页面内容转成pdf

2.然后下载

五、html-docx-js

参考网址:

https://github.com/evidenceprime/html-docx-js

http://evidenceprime.github.io/html-docx-js/test/sample.html

代码

import React, { Component } from "react";
import { Checkbox, Table, Button, Icon } from "semantic-ui-react";
import "semantic-ui-css/semantic.min.css";
import htmlDocx from 'html-docx-js/dist/html-docx';
import {saveAs} from 'file-saver';
import imgTest from "./1.png"
class App extends Component {download() {this.convertImagesToBase64();var contentDocument = '<!DOCTYPE html>' + document.getElementById("docReport-container").outerHTML;var converted = htmlDocx.asBlob(contentDocument, {orientation: "Portrait"});saveAs(converted, 'study.docx');}convertImagesToBase64 () {var regularImages = document.querySelectorAll("img");var canvas = document.createElement('canvas');var ctx = canvas.getContext('2d');[].forEach.call(regularImages, function (imgElement) {// preparing canvas for drawingctx.clearRect(0, 0, canvas.width, canvas.height);canvas.width = imgElement.width;canvas.height = imgElement.height;ctx.drawImage(imgElement, 0, 0);// by default toDataURL() produces png image, but you can also export to jpeg// checkout function's documentation for more detailsvar dataURL = canvas.toDataURL();imgElement.setAttribute('src', dataURL);})canvas.remove();}render() {return (<div id="docReport-container"><Button className="dowButton" basic onClick={() => this.download()}><Icon name="download" />Download Report</Button><div><div className="report_table_box"><div className="user_report_title"><strong>Summary</strong></div></div><Table celled className="report_table"><Table.Header><Table.Row><Table.HeaderCell className="user_report_table_th_first">Siren ID + Patient Initials</Table.HeaderCell><Table.HeaderCell className="user_report_table_th">Report</Table.HeaderCell><Table.HeaderCell className="user_report_table_th user_report_table_th_last">Bill?</Table.HeaderCell></Table.Row></Table.Header><Table.Body><Table.Row><Table.Cell className="user_report_table_td_first">1</Table.Cell><Table.Cell className="user_report_table_td">8/22019</Table.Cell><Table.Cell className="user_report_table_td"><Checkbox name="selectSite" /></Table.Cell></Table.Row></Table.Body></Table><img src={imgTest} alt="no img"></img></div></div>);}
}
export default App;
  • 检索不到外部css文件,同时并不是支持所有css 属性

解决html-docx-js不能引入外部css的问题。注意哦,尽量少用别人的样式组件,样式尽量自己写的简单css

注意:这个css文件需要另外粘贴一份到public文件夹内

download() {this.convertImagesToBase64();let getIndex = new Promise((resolve, reject) => {fetch('./App.css').then(data => {data.text().then(css => {resolve(css);})}).catch(error => reject(error));});getIndex.then(css => {let html ='<!DOCTYPE html><html><head lang="en"><style></style>' +'<meta charset="UTF-8"><title>Report</title><link rel="stylesheet" href="//cdn.jsdelivr.net/npm/semantic-ui@2.4.2/dist/semantic.min.css" /></head><body>' +document.getElementById("docReport-container").outerHTML +'</body></html>';html = juice.inlineContent(html, css);let docx = htmlDocx.asBlob(html);saveAs(docx, 'report.docx');}).catch(error => {console.log(error);});// var contentDocument = '<!DOCTYPE html>' + document.getElementById("docReport-container").outerHTML;// var converted = htmlDocx.asBlob(contentDocument, { orientation: "Portrait" });// saveAs(converted, 'study.docx');}
  • 复选框和单选框不能打印出来

直接用图片代替即可

<img src={checkBlank} alt="error" className="checkbox_img" />
  • 下载的表格出现背景颜色不填充满,线条没有紧密相连得问题

给table的css添加border-collapse: collapse;

在table元素上直接设置

cellPadding="0"

cellSpacing="0"

border="0"

那么表格的边框线会紧密相连。目前还没解决背景颜色不铺满的问题。有时间再来更新

 

六、wkhtmltopdf

缺点:wkhtmltopdf不支持动态页面或者react

优点参考:https://juejin.im/post/5b08bbb8f265da0dc562f046#heading-0

 

 

 

 

 

 

 

 

 

 

  相关解决方案