MHTML文件如何在前端页面展示
MHTML文件如何在前端页面展示
需求背景:
目前在给证券公司做项目,但是在使用新系统的过程中,甲方还希望之前之前系统的历史记录可以看到。
最初制定的计划是项目组里面做数据的把原系统页面爬取下来,转成图片,直接给到前端即可。但是产品建议说把整个页面的html爬下来再展示更好,但是在爬取html的过程中出现了若干问题,总之最后做数据的把之前的历史记录爬成MHTML格式的给了后端,我在请求的时候后端把MHTML格式文件内容以字符串格式全部给到前端。
技术背景:
1. MHTML和HTML
特性 | HTML | MHTML |
---|---|---|
文件格式 | 纯文本文件,扩展名为.html 或.htm | 单一文件,扩展名为.mht 或.mhtml |
资源处理 | 外部资源通过链接引用 | 所有资源(如图片、CSS、JS)嵌入文件中 |
用途 | 用于创建和展示网页 | 用于保存完整网页,适合离线浏览 |
兼容性 | 所有浏览器均支持 | 部分浏览器支持(如IE、Edge) |
文件大小 | 较小,因资源未嵌入 | 较大,因包含所有资源 |
离线支持 | 需要外部资源在线访问 | 支持离线浏览,资源已嵌入 |
分享与保存 | 需附带外部资源 | 单一文件,便于分享和保存 |
总结:
- HTML:适合在线浏览,依赖外部资源。
- MHTML:适合保存完整网页,便于离线使用和分享。
2. mhtml2html
需要了解这个库的使用方式,其官网地址如下:mhtml2html - npm
3. 为什么需要 .window.document
?
原因 1:模拟浏览器环境
mhtml2html
的设计可能是为了在 Node.js 或浏览器中模拟一个独立的浏览器环境(类似jsdom
或iframe
),以正确解析 MHTML 文件的资源(如 CSS、图片、脚本等)。- 在这种情况下,生成的
convertedHTML
对象会包含一个完整的window
对象,其内部才有document
和documentElement
。
原因 2:隔离文档作用域
- MHTML 文件可能包含独立的 HTML、CSS 和 JavaScript 代码,需要在一个隔离的环境中运行,避免污染当前页面的全局作用域。
- 通过将文档封装在
window
对象中,mhtml2html
实现了这种隔离。
4. innerHTML和outerHTML
特性 | innerHTML | outerHTML |
---|---|---|
定义 | 获取或设置元素内部的 HTML 内容(不包含该元素本身) | 获取或设置整个元素的 HTML,包括该元素自身及其所有子元素 |
返回内容 | 返回元素内部的 HTML 字符串 | 返回包含当前元素标签及其内部 HTML 的完整字符串 |
修改行为 | 赋值后仅会替换元素内部的内容,不会改变元素自身 | 赋值后会替换整个元素(即当前元素及其所有子元素都会被更新或移除) |
常见用途 | 用于更新或操作元素内部的内容,例如动态加载文本或子节点 | 用于替换、删除整个元素,或将元素及其内容导出为 HTML 字符串 |
注意事项 | 修改后元素本身的标签和属性保持不变,仅子节点内容发生变化 | 修改后原来的 DOM 引用可能失效,因为整个元素可能已经被新元素替换 |
解决思路:
1. 使用iframe展示内容
1. 首先,你可能需要一个库来解析MHTML文件。可以使用`mhtml2html`库来将MHTML转换为HTML。
2. **Blob**: 用于创建一个包含HTML内容的Blob对象。
3. **URL.createObjectURL**: 生成一个临时的URL,用于在`iframe`中加载HTML内容。
核心代码示例:
<template>
<div>
<!-- 使用 iframe 展示 MHTML 内容 -->
<iframe :src="iframeSrc" width="100%" height="500px"></iframe>
</div>
</template>
<script>
import { ref, onMounted } from "vue";
import mhtml2html from "mhtml2html";
export default {
setup() {
const iframeSrc = ref(""); // 用于存储 iframe 的 src
// 加载并解析 MHTML 文件
const loadMHTML = async () => {
try {
// 假设你有一个 MHTML 文件的 URL
const mhtmlUrl = "/path/to/your/file.mhtml";
// 获取 MHTML 文件内容
const response = await fetch(mhtmlUrl);
const mhtmlContent = await response.text();
// parse解析:将 MHTML 字符串解析为对象
const parsedMHTML = mhtml2html.parse(mhtmlContent);
// convert, 将 解析出的 MHTML 对象转译成含有资源的 HTML
const convertedHTML = mhtml2html.convert(parsedMHTML);
if (!convertedHTML?.window.document?.documentElement) {
throw new Error("HTML转换失败,未找到可用的 documentElement");
}
// 注意:mhtml2html.convert 返回的是一个模拟的浏览器环境,其文档对象 (document) 必须通过 .window 访问。这种设计是为了隔离 MHTML 文件的内容,确保资源解析和样式作用域的正确性。直接使用 convertedHTML.document 会失败,因为 document 并未直接暴露在返回值顶层。
const htmlContent =
convertedHTML.window.document.documentElement.outerHTML;
loadingText.value = "解析成功,开始展示";
// 创建 Blob URL
const blob = new Blob([htmlContent], { type: "text/html" });
iframeSrc.value = URL.createObjectURL(blob);
} catch (error) {
console.error("Failed to load MHTML file:", error);
}
};
// 在组件挂载时加载 MHTML 文件
onMounted(() => {
loadMHTML();
});
// 在组件销毁时清理 Blob URL
onBeforeUnmount(() => {
if (iframeSrc.value) {
URL.revokeObjectURL(iframeSrc.value);
}
});
return {
iframeSrc,
};
},
};
</script>
<style scoped>
iframe {
border: 1px solid #ccc;
}
</style>
2. 使用v-html展示内容
本来想将解析出来的html文本内容直接通过v-html
展示,但是实际展示却是html字符串,所以不推荐使用这种方式。
总结:
实际上这个功能不算复杂,但是对于一些基础的前端知识先需要了解,否则会踩坑,有较长的试错时间。