使用Node解析EML文件
文章目录
- 什么是EML文件
- 实现EML导入
- 就这?
- winmail.dat
- 编码
什么是EML文件
通过的说,电子邮件导出后的文件格式就是.eml
文件,比如使用outlook
、163邮箱
等等电子邮件程序将电子邮件导出后,就可以得到.eml
文件,EML文件应该符合RFC 5322规范,这样EML文件就可以在不同的邮件客户端之间流通。
也就是说,使用163
邮箱客户端导出的eml
文件,完全可以在Ooutlook中『基本一致』的打开
之所以说基本一致,因为还是有很多兼容性问题要处理,而且很多邮箱客户端还有独有的私有协议内容,对应的样式在其他邮箱客户端只能以基本的样式进行展示
以163邮箱为例,如下图,将邮件导出,就得到了预约直播,听专家分享《面向大规模数据的云端管理,百度沧海·存储产品解析》.eml
文件
使用邮箱大师,就可以将这个文件导入,并进行展示:
展示与原邮件基本是一致的
实现EML导入
在我们的灵犀办公客户端上,也要实现这个导入EML文件的功能,这个功能有现成的库来支持,可选择的有三个库:
- NodeMailer的MailParser
- eml-format
- eml-parser
三个工具使用的解析的结果大同小异,我们选用的是MailParser(额外说一句,NodeMailer功能非常强大,可以用它构建一个自己的邮件收发系统)
按照文档很容易就能实现一个简单的EML解析器:
const fse = require('fs-extra');
const simpleParser = require('mailparser').simpleParser;function parseEml(filePath) {return fse.stat(filePath).then(() => {return fse.readFile(filePath).then((file) => {return simpleParser(file);});});
}
解析的结果包含了下面的字段:
在我们的系统中,根据这些属性,可以构造出一个完整的MailEntitiy结构,用于完成常规的服务端返回的邮件数据解析流程。到此解析就流程就完了。
就这?
当然不是,为了提高解析性能,我们还在客户端本地的数据库,使用文件地址和文件名称组合为key
,在本地数据库记录了结果。当然这都是小优化,直到功能上线后,QA报了一个线上问题,有一封邮件,我们的客户端打开后,样式乱了,结果如下:
导入导服务端之后却是正常的
用邮箱大师也是正常的,那我们出了什么问题呢?
用MailParser解析之后,发现解析的结果里面,attachments
是有值的,而html
是false
,text
里面就是我们的客户端解析后的数据,并没有相应的内联样式:
一开始以为是参数的问题,一通调试后发现然并卵,后来以为是编码的问题,因为这封邮件的charset
是gb2312
,结果发现也没有用(虽然后面编码还是要处理的)
咨询了服务端的同学,他给出的答案一针见血,邮件的附件里面是不是有一个dat
附件,需要对dat
附件进行二次解析才可以
winmail.dat
winmai.dat
是Microsoft Outlook是特殊格式附件,如果Outlook寄出富文本格式的信件,其他邮件客户端收到的时候,就会产生这个文件。真正的内容和样式实际上都是在这个附件中。
所以需要对这个文件进行二次解析,在NPM上找到了node-tnef这个工具,使用它就可以解析winmai.dat
附件。由于我们在解析eml
文件时,附件已经是Buffer
格式的数据了,所以直接使用tnef.parseBuffer
这个方法就可以,得到的content
里面包含了BodyHTML
数据,把这个带有样式的富文本数据格式赋值给eml
解析后的html
字段(原本是false
)就可以解析出正确的数据了
function handleDatAttachments(parsedMail, encoding) {const datAttach = parsedMail.attachments.find((v) => v.contentType === 'application/ms-tnef' && v.filename && v.filename.endsWith('dat'));if (datAttach) {return new Promise((resolve) => {tnef.parseBuffer(datAttach.content, (err, content) => {if (err) {resolve('');} else {const res = content.BodyHTML.toString();resolve(res);}});});}
}function parseEml(filePath) {return fse.stat(filePath).then(() => {return fse.readFile(filePath).then((file) => {return simpleParser(file).then((parsedMail) => ({parsedMail}));});});
}parseEml(path.join(__dirname, 'test3.eml')).then(({encoding, parsedMail}) => {console.timeEnd('parse time');return writeFile('parsed.js', JSON.stringify(parsedMail)).then(() => {return handleDatAttachments(parsedMail, encoding).then((html) => {if (html) {parsedMail.html = html;}return parsedMail;});});}).then((parsedMail) => writeFile('parsed.html', parsedMail.html));
前面先对附件进行了一下过滤,只针对这种特殊的附件进行处理,其他的附件处理时有自己的逻辑。解析完成后,在本地模拟时,使用了一个iframe
来加载内容:
<!-- index.html -->
<!DOCTYPE html>
<html lang='en'>
<head><meta charset='UTF-8'><title>Title</title><style>* {padding: 0;margin: 0;}html, body {width: 100%;height: 100%;overflow: hidden;}.iframe {border: none;}</style>
</head>
<body><iframe class='iframe' src='parsed.html' width='100%' height='100%'></iframe>
</body>
</html>
结果发现,表格样式可以加载出来了,但是文本都成了乱码了:
这就是前面说的编码的问题了
编码
在Webstorm里面直接打开test3.eml
文件,可以看到这样的一段内容:
--_000_12ff39986d6b48b7a0ff65d2447f3c87ghaccn_
Content-Type: text/plain; charset="gb2312"
Content-Transfer-Encoding: base64
里面的charset
是gb2312
,但是我们的index.html
设置的charset
是UTF-8
,二者不匹配,自然就出现了问题。
这就需要我们将解析得到的gb2312
编码的html
内容转换为utf-8
编码。由于Node原生不支持GBK编码,所以使用了iconv-lite这个工具来进行编码的转换,使用很简单:
const iconvLite = require('iconv-lite');
// 将 data 中的内容以 gbk 编码格式转换为默认使用 utf-8 编码的字符串
const newData = iconvLite.decode(data, 'gbk')
所以在上面的代码中加入这一部分,此外还要注意的是,解析得到的HTML内容中是有一个<meta>
指定了charset=gb2312
的编码,也会导致页面解析出问题,所以需要给它替换掉。
这里直接使用了字符串的查找和替换,也可以使用DomParser
来进行文档的二次处理,加上这部分内容之后,显示终于正常了
完整的代码:
/*** Created by zh on 2022/6/1.*/
const fse = require('fs-extra');
const path = require('path');
const simpleParser = require('mailparser').simpleParser;
const tnef = require('node-tnef');
const iconvLite = require('iconv-lite');function parseEml(filePath) {return fse.stat(filePath).then(() => {return fse.readFile(filePath).then((file) => {const charsetMatch = file.toString().match(/Content-Type:.+charset="(.+)"/i);const encoding = Array.isArray(charsetMatch) && charsetMatch[1] ? charsetMatch[1] : 'utf8';return simpleParser(file).then((parsedMail) => ({parsedMail,encoding}));});});
}function handleDatAttachments(parsedMail, encoding) {const datAttach = parsedMail.attachments.find((v) => v.contentType === 'application/ms-tnef' && v.filename && v.filename.endsWith('dat'));if (datAttach) {return new Promise((resolve) => {tnef.parseBuffer(datAttach.content, (err, content) => {if (err) {resolve('');} else {const buffer = iconvLite.decode(Buffer.from(content.BodyHTML), encoding);const res = buffer.toString();resolve(res);}});});}
}function writeFile(fileName, data) {const filePath = path.join(__dirname, fileName);return fse.writeFile(filePath, data, 'utf8').then(() => {console.log('写入完成', fileName);});
}console.time('parse time');
parseEml(path.join(__dirname, 'test3.eml')).then(({encoding, parsedMail}) => {console.timeEnd('parse time');return writeFile('parsed.js', JSON.stringify(parsedMail)).then(() => {return handleDatAttachments(parsedMail, encoding).then((html) => {if (html) {parsedMail.html = encoding !== 'utf-8' ? html.replace(/charset=gb2312/i, /charset=utf-8/) : html;}return parsedMail;});});}).then((parsedMail) => writeFile('parsed.html', parsedMail.html));
也可以在这里看到源码。
使用Node解析EML文件相关推荐
- java解析eml文件_使用JavaMail解析EML文件
Java 当我们在outlook中保存一个邮件是可以存成eml格式,这种格式是标准的邮件格式. 这种文件可以用JavaMail来解析. import java.util.*; import java. ...
- Java解析eml文件工具类
依赖 <!-- https://mvnrepository.com/artifact/javax.mail/mail --><dependency><groupId> ...
- python 解析 eml文件
#-*- encoding: gb2312 -*- import email fp = open('xxxx.eml', "r") msg = email.message_from ...
- java eml解析_javamail 收邮件 解析eml文件
内容来自:http://www.oschina.net/bbs/thread/528 以下代码经过测试了的能通过 我在用javaMail做收邮件时怎么邮件内容重复(一种文本格式的,一种html格式的) ...
- Java实现eml文件的解析
最近在做邮件归档,然后需要解析邮件导出的eml,记录每封邮件的归档时间,发件人.标题.发件时间.归档的目录 以下是一个demo示例:待完成此功能后再优化后续代码 import java.io.File ...
- C# 操作.eml文件
前段时间做了个关于邮件的东西,记录一下如何解析和保存eml文件的. /// <summary> /// 解析eml /// </summary> ...
- 通过Node.js解析stl文件
通过Node.js解析stl文件 在此之前请确认已安装Node.js,已配置环境变量.如未安装与配置,请查看Node.js安装及环境配置(Windows) 一.下载Node项目文件 Node项目文件下 ...
- Java解析eml邮件格式文件
基本介绍 关于邮件的需求总是以邮件发送或接收为主,之前涉及的技术选型有Java Mail.Apache Commons Email.Spring Mail,由于工作上的需要对eml格式的文件进行解析, ...
- 如何解析EML(邮件)格式的文件以及一款小巧的EML邮件阅读工具
在理解EML格式的时候,先回顾一下历史,这样有助于理解邮件的格式,比如邮件传输时为何会有多种编码方式.此外,理解EML格式也有助于理解HTTP协议. 历史溯源 由于历史原因,我们目前看到的大部分的网络 ...
最新文章
- 这个最基本的生命细节才被揭开——25毫秒核孔穿梭
- 相对路径和绝对路径错误造成的漏洞
- 《精通Unreal游戏引擎》一第4步 使用BSP创建地图
- 笔记-项目质量管理-过程决策程序图法
- 【字符串系列】字符串匹配中的位并行算法
- Python基础教程:__setattr__和__delattr__和__getattr__的用法
- 如何使用Navicat MySQL导入.sql文件
- 个税改革怎么改?媒体称年收入超12万者税负或增加
- 如何用python刷屏_利用python实现在微信群刷屏的方法
- CCF推荐国际学术会议和期刊目录2019年
- 教育实习手册高中计算机,计算机教育实习论文大纲格式模板 计算机教育实习论文框架如何写...
- python3视频教程哪个好_2020年5个经典python编程入门视频教程推荐学习
- 【CVPR 2021】Revisiting Knowledge Distillation: An Inheritance and Exploration Framework
- 多态的表现形式有哪些?
- Word怎么在方框里打勾就是一个方框打上一个对号
- 彻底卸载mac软件的方法,这样才删除干净哦
- Gym - 100519 B Bring Your Own Bombs 离散化+二分+思维
- 阿里云服务器与堡垒机搭建Oracle数据库(配置多个实例)以及数据库导入、导出
- Linux中nohup的使用
- 一周内咸鱼疯转2.4W次,最终被所有大厂封杀