接口返回html,前端如何实现预览并下载成多种格式?
需求简介
昨天遇到一个很小众的需求:点击一个按钮后,请求一个接口,接口返回html,需要实现这个html的预览与下载。
这个功能其实很简单,无非就是展示一个html和纯前端实现一个文件的下载。想着有些人可能刚入门,为了不让他们去某N搜索,就简单分享一下自己的实现方案吧!
技术方案分析
接口如果直接返回一个html
,说明这个接口本身就是一个后端实现的网页,可以直接在浏览器访问的。
那么实现这个接口返回的html预览就有两个很简单的办法:
- 使用iframe直接展示
- 请求接口,将其返回的html渲染:vue直接使用v-html渲染或者react使用dangerouslySetInnerHTML渲染!
下载的话,不同格式有不同方案:
下载html源文件,可以直接创建一个a标签;下载为图片的话,可以使用htmlCanvas;下载为pdf的话,可以使用htmlCanvas + pdfjs
实现html预览
假设这个接口名为http://127.0.0.1:5500/src/pages/flex/IterableEvents/
iframe方式
使用iframe是最简单的一种预览方式,而且基本不存在什么样式或者跨域问题!
react
const Component = () => {
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
return (
<div style={{ height: '600px', padding: '20px 0' }}>
<iframe style={{ width: '100%', height: '100%' }} src={url}></iframe>}
</div>
)
}
vue2
<template>
<div :style="{ height: '600px', padding: '20px 0' }">
<iframe :style="{ width: '100%', height: '100%' }" :src="url"></iframe>
</div>
</template>
<script>
export default {
data() {
return {
url: 'http://127.0.0.1:5500/src/pages/flex/IterableEvents/'
};
}
};
</script>
请求接口,数据渲染方式
react写法
首先,我们进行数据请求
const Component = () => {
const [htmlContent, setHtmlContent] = useState('')
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
useEffect(() => {
fetch(url)
.then((response) => response.text())
.then((data) => {
// data就是接口返回的数据
console.log('data: ', data);
setHtmlContent(data)
})
.catch((error) => {
console.error('Error fetching HTML:', error)
})
}, [])
// 渲染html
return ()
}
上述代码中,我们使用fetch对接口进行了请求,其返回数据就是一个html文本
返回的文本我们储存在了htmlContent中,现在,我们使用react的dangerouslySetInnerHTML进行渲染。
在React中,dangerouslySetInnerHTML用于直接设置HTML内容,它允许你将一个HTML字符串插入到DOM中。这通常是为了渲染从服务器接收到的HTML内容,或者是需要插入动态生成的HTML字符串。
const Component = () => {
const [htmlContent, setHtmlContent] = useState('')
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
useEffect(() => {
fetch(url)
.then((response) => response.text())
.then((data) => {
// data就是接口返回的数据
console.log('data: ', data);
setHtmlContent(data)
})
.catch((error) => {
console.error('Error fetching HTML:', error)
})
}, [])
return (
<div style={{ width: '100%', height: '100%', overflow: 'auto' }}>
<div dangerouslySetInnerHTML={{ __html: htmlContent }} style={{ width: '100%' }}></div>
</div>
)
}
vue写法
vue的实现过程和react是几乎一致的
<template>
<div :style="{ width: '100%', height: '100%', overflow: 'auto' }">
<div v-html="htmlContent" :style="{ width: '100%' }" ></div>
</div>
</template>
<script>
export default {
name: 'MyComponent',
data() {
return {
htmlContent: ''
};
},
created() {
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/";
fetch(url)
.then(response => response.text())
.then(data => {
console.log('data: ', data);
this.htmlContent = data;
})
.catch(error => {
console.error('Error fetching HTML:', error);
});
}
};
</script>
这种方式渲染出的页面和iframe几乎是一致的:
但要注意,接口不能跨域,跨域的话,必然是渲染不出来的!
实现下载
下载成html源文件
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
// 下载
const downLoad = async () => {
try {
const link = document.createElement('a')
link.href = url
link.click()
} catch (error) {
message.error('Download failed')
}
}
这种方式非常简单,其实就是借助a标签实现了文件下载,但注意url不能跨域!
下载成图片
注意:下载的前提是接口不跨域
下载成图片可以借助htmlcanvas实现截图下载功能
首先需要安装这个插件
npm i html2canvas
iframe形式
import html2canvas from 'html2canvas'
const Component = () => {
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
// 下载
const downLoad = async () => {
try {
const iframe = document.getElementById('myPage')
targetHtml = iframe?.contentDocument || iframe?.contentWindow?.document
// scale可以调整图片清晰度
html2canvas(targetHtml, { scale: 1, useCORS: true })
.then((canvas) => {
const imgData = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = imgData;
link.download = 'XXXXX.png';
link.click();
})
} catch (error) {
message.error('Download failed')
}
}
return (
<div style={{ height: '600px', padding: '20px 0' }}>
<iframe style={{ width: '100%', height: '100%' }} src={url} id='myPage'></iframe>}
</div>
)
}
vue写法和这个几乎一样,把downLoad方法照抄就行!
请求渲染的方式
和iframe的写法完全一致,改一下targetHtml的获取就行!
import html2canvas from 'html2canvas'
const Component = () => {
const [htmlContent, setHtmlContent] = useState('')
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
// 下载
const downLoad = async () => {
try {
const targetHtml = document.getElementById('myPage')
// scale可以调整图片清晰度
html2canvas(targetHtml, { scale: 1, useCORS: true })
.then((canvas) => {
const imgData = canvas.toDataURL('image/png');
const link = document.createElement('a');
link.href = imgData;
link.download = 'XXXXX.png';
link.click();
})
} catch (error) {
message.error('Download failed')
}
}
useEffect(() => {
fetch(url)
.then((response) => response.text())
.then((data) => {
setHtmlContent(data)
})
.catch((error) => {
console.error('Error fetching HTML:', error)
})
}, [])
return (
<div style={{ width: '100%', height: '100%', overflow: 'auto' }}>
<div dangerouslySetInnerHTML={{ __html: htmlContent }} style={{ width: '100%' }} id='myPage'></div>
</div>
)
}
下载成pdf
注意:下载的前提是接口不跨域
react写法
下载成pdf,首先我们需要先下载成图片,然后借助pdfjs实现。我们需要安装html2canvas和pdfjs两个插件
npm i html2canvas
npm i pdfjs
import html2canvas from 'html2canvas'
import jsPDF from 'jspdf
const Component = () => {
const [htmlContent, setHtmlContent] = useState('')
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/"
// 下载
const downLoad = async () => {
try {
const targetHtml = document.getElementById('myPage')
const doc = new jsPDF();
html2canvas(targetHtml, { scale: 1, useCORS: true })
.then((canvas) => {
const imgData = canvas.toDataURL('image/png');
const imgWidth = 210;
const pageHeight = 295;
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save('webpage.pdf')
message.destroy()
setConfirmLoading(false)
})
.catch((error) => {
})
} catch (error) {
message.error('Download failed')
}
}
useEffect(() => {
fetch(url)
.then((response) => response.text())
.then((data) => {
setHtmlContent(data)
})
.catch((error) => {
console.error('Error fetching HTML:', error)
})
}, [])
return (
<div style={{ width: '100%', height: '100%', overflow: 'auto' }}>
<div dangerouslySetInnerHTML={{ __html: htmlContent }} style={{ width: '100%' }} id='myPage'></div>
</div>
)
}
图片下载成都是pdf都是社区成熟的方案,代码懂了,可以直接百度!
vue写法
<template>
<div :style="{ width: '100%', height: '100%', overflow: 'auto' }">
<div v-html="htmlContent" :style="{ width: '100%' }" ref="myPage"></div>
<button @click="downLoad">Download as PDF</button>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
export default {
name: 'MyComponent',
setup() {
const htmlContent = ref('');
const myPage = ref(null);
const url = "http://127.0.0.1:5500/src/pages/flex/IterableEvents/";
const downLoad = async () => {
try {
const targetHtml = myPage.value;
const doc = new jsPDF();
const canvas = await html2canvas(targetHtml, { scale: 1, useCORS: true });
const imgData = canvas.toDataURL('image/png');
const imgWidth = 210; // A4 paper width in mm
const pageHeight = 295; // A4 paper height in mm
const imgHeight = canvas.height * imgWidth / canvas.width;
let heightLeft = imgHeight;
let position = 0;
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
while (heightLeft >= 0) {
position = heightLeft - imgHeight;
doc.addPage();
doc.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight);
heightLeft -= pageHeight;
}
doc.save('webpage.pdf');
} catch (error) {
console.error('Download failed:', error);
}
};
onMounted(() => {
fetch(url)
.then(response => response.text())
.then(data => {
htmlContent.value = data;
})
.catch(error => {
console.error('Error fetching HTML:', error);
});
});
return {
htmlContent,
myPage,
downLoad
};
}
};
</script>
总结
这篇文章分享了如何将一个接口返回的html进行渲染并下载,方案都很朴实无华,最简单的还是iframe的方式!想相信能对各种有一点帮助!感谢大家的关注!
转载自:https://juejin.cn/post/7369868541934845967