之前有一个需求是点击导出按钮,然后去各个页面截图,将图片传给后端,后端返回文件流,导出ppt报告。这个需求听到的时候头都大了,不知道怎么做,之后有思路了,在实现过程中也出现了各种问题,最后还是做完了( 撒花!)
一、首先来说一下单个页面截图
单个页面截图是比较简单的,通过插件domtoimage可以实现。
1.安装插件
yarn add dom-to-image
2.在需要截图的页面jsx里面引入该插件
import domtoimage from "dom-to-image";
3.截图
async function check() {let dom_policy = document.getElementById("policy");let formData_total = new FormData();if (dom_policy) {// 执行dom加载完成后的操作,例如echart的初始化操作await domtoimage.toPng(dom_policy).then((dataUrl) => {let formData = new FormData();let img = base64ToBlob(dataUrl, "命名.png");formData.append("port_presentation", img);if (formData.has("port_presentation")) {formData_total.append("area_review",formData.getAll('port_presentation'));}});
}
}
这里对代码进行一些解释:
(1)因为在该页面我需要截图两张,所以我在截图前创建一个fromData来存储两张截图
let formData_total = new FormData();
formData_total相当于就是一个对象。
(2)每次截图完我需要一个对象来保存我的图片,因此在每个截图操作里面,我又创建了一个formData来存储
let formData = new FormData();
(3)因为new FromData()出来的相当于是一个对象,因此需要属性和属性值。
formData.append("port_presentation", img);
(4)将formData里面的值展开
formData.getAll('port_presentation')
(5)base64ToBlob是将截图的数据进行转化
let img = base64ToBlob(dataUrl, "命名.png");
(6)补充一下,formData.has可以检测当前属性是否有值
formData.has('port_presentation')
截图完了,就可以发送请求给后端了
这里我采用的是fetch发送请求
fetch(window.location.origin + `请求路径`, {responseType: "blob",method: "post",body: formData_total,}).then((res) => {const filename = res.headers.get("content-disposition").split(";")[1].split("=")[1];res.blob().then((blob) => {const link = document.createElement("a");link.style.display = "none";// a 标签的 download 属性就是下载下来的文件名link.download = filename;link.href = URL.createObjectURL(blob);document.body.appendChild(link);link.click();// 释放的 URL 对象以及移除 a 标签URL.revokeObjectURL(link.href);document.body.removeChild(link);});}).catch((res)=>{alert('导出失败')})
二、接下来说说多个页面截图
多个页面截图就是这个页面截图完了,自动跳到下个页面截图,一直到所有需要截图的页面截完为止,再发送请求。
我的框架是react,因此我用的状态机将各个页面联系起来。去改变状态机的值,让截图页面接受截图信号,进行自动跳转截图。
1.状态机,初始值设置为"导出ppt"
2. 点击导出按钮时,通过navigate进行路由跳转,并改变保存在状态机的值
dispacth(pptActions("下一个页面的命名(比如:你好react)"));setTimeout(() => {navigate("下一个页面的路由");}, 3000);
3.自动跳转到下一个页面时,需要去获取刚刚保存到状态机的值,是否为"你好react",进行截图信号判断,执行截图函数check()。
//截图信号const ppt_key = useSelector((state) => {return state.ppt;});useEffect(() => {if (ppt_key == "你好react") {setTimeout(()=>{check();},3000)}}, [ppt_key]);
4.截图函数跟单页面差不多,但是由于会涉及到几十张截图,数量较大,因此我单独写了一个js文件来进行汇总截图文件。
async function check() {let dom_policy = document.getElementById("policy");let dom_map = document.getElementById("map");// console.log(dom_policy);let formData_total = new FormData();if (dom_policy && dom_map) {// 执行dom加载完成后的操作,例如echart的初始化操作await domtoimage.toPng(dom_policy).then((dataUrl) => {let formData = new FormData();let img = base64ToBlob(dataUrl, "命名.png");formData.append("port_presentation", img);if (formData.has("port_presentation")) {formData_total.append("area_review",formData.getAll('port_presentation'));summaryFormData(formData.getAll('port_presentation'),'',"命名.png");}});if(formData_total.has('area_review')){dispacth(pptActions('下一个页面的命名'));setTimeout(()=>{navigate('下一个页面的路径');},5000)}
}
}
汇总js文件
let formData_ppt = new FormData();export const summaryFormData = (value, title, name, result) => {if (name == "命名.png" ||name == "命名.png" ||name == "命名.png") {formData_ppt = new FormData();}//为了分段发送请求,一次性发送大量数据会有问题,部分数据拿不到,因此这里就分成了3段let blob = new Blob(value);let file = new File([blob], name, { type: "image/png" });//这里根据后端需要需要加type类型
//这里根据后端需要的参数名,进行属性和属性值的添加
if (title == "") {formData_ppt.append("area_review", file);} else if (title == "") {formData_ppt.append("edition_set", file);} else if (title == "") {formData_ppt.append("trend_predict", file);} else if (title == "") {formData_ppt.append("zero_carbon", file);}if (result == "完") {formDataFounction(formData_ppt, 0);}
};function formDataFounction(value, num) {if (num == 0) {fetch(window.location.origin + `请求路径`, {method: "post",body: value,}).then((response) => response.json()).then((data) => {console.log(data)}).catch((res) => {alert("导出失败");});} else {fetch(window.location.origin + `请求路径`, {responseType: "blob",method: "post",body: value,}).then((res) => {localStorage.removeItem("edition_list");localStorage.removeItem("scene_list");localStorage.removeItem("number");const filename = res.headers.get("content-disposition").split(";")[1].split("=")[1];res.blob().then((blob) => {const link = document.createElement("a");link.style.display = "none";// a 标签的 download 属性就是下载下来的文件名link.download = filename;link.href = URL.createObjectURL(blob);document.body.appendChild(link);link.click();// 释放的 URL 对象以及移除 a 标签URL.revokeObjectURL(link.href);document.body.removeChild(link);document.body.removeChild(exportPpt);});}).catch((res)=>{document.body.removeChild(exportPpt);alert('导出失败')})}
}
之后的每个页面都重复3、4,当一个阶段截图完成时,就发送一个信号让汇总js文件知道,可以发送请求了。我这里用的是多传一个参数"完"。
这里需要注意的是,用fetch发送请求,想要拿到返回的data数据,需要先执行
.then((response) => response.json())
才可以拿到。
具体参照上面的代码吧。
可能没说清楚,主要是太多了,有问题评论区讨论哦。