如何在Node.js中通过Google API获取并格式化Gmail邮件?
问题:如何渲染与Gmail一致格式的邮件内容?
我正在使用Node.js中的Google API库获取Gmail邮件,以下代码片段展示了我如何检索并提取邮件数据:
const gmail = google.gmail({ version: "v1", auth: oAuth2Client }); const messageId = req.params.id; const message = await gmail.users.messages.get({ userId: "me", id: messageId, format: "full", }); // Extract the required data from the message const isRead = !message.data.labelIds.includes("UNREAD"); const snippet = message.data.snippet.trim(); const email = { id: message.data.id, subject: message.data.payload.headers.find( (header) => header.name === "Subject" ).value, from: message.data.payload.headers .find((header) => header.name === "From") .value.split("<")[0], date: moment( message.data.payload.headers.find((header) => header.name === "Date").value ).format("MM/DD/YYYY, hh:mm a"), snippet, isRead, message: Buffer.from(message.data.payload.parts[0].body.data, "base64").toString("utf-8"), };
上述代码可成功获取邮件并提取主题、发件人、日期及邮件内容等关键信息,但提取出的message为原始格式,无法保留Gmail界面中的原有排版与格式。我希望能实现与Gmail网页端或官方App一致的邮件渲染效果,请问该如何对获取到的邮件进行格式化处理?
解决思路与改进方案
1. 优先获取HTML格式的邮件内容
Gmail邮件通常以multipart/alternative格式存在,同时包含纯文本(text/plain)和富文本(text/html)版本。需要递归遍历邮件的parts结构,找到text/html类型的内容部分,解码其Base64数据后即可得到带排版格式的HTML内容。
2. 处理内嵌资源(如图片)
邮件中的内嵌图片通常使用cid:协议引用附件,需要将这些CID替换为对应的Base64 Data URI,这样HTML才能直接渲染图片。
3. 安全净化HTML内容
直接渲染外部邮件的HTML存在XSS攻击风险,必须使用HTML净化库(如DOMPurify)过滤掉不安全的标签和脚本。
改进后的代码示例
const gmail = google.gmail({ version: "v1", auth: oAuth2Client }); const messageId = req.params.id; const DOMPurify = require('dompurify'); const { JSDOM } = require('jsdom'); const moment = require('moment'); // 初始化DOMPurify用于HTML净化 const window = new JSDOM('').window; const purify = DOMPurify(window); const message = await gmail.users.messages.get({ userId: "me", id: messageId, format: "full", }); // 封装获取Header的工具函数,避免重复代码 const getHeaderValue = (headerName) => { const header = message.data.payload.headers.find(h => h.name === headerName); return header?.value || ''; }; // 提取基础邮件信息 const isRead = !message.data.labelIds.includes("UNREAD"); const snippet = message.data.snippet.trim(); const email = { id: message.data.id, subject: getHeaderValue('Subject'), from: getHeaderValue('From').split("<")[0], date: moment(getHeaderValue('Date')).format("MM/DD/YYYY, hh:mm a"), snippet, isRead, plainTextMessage: '', // 纯文本内容(备用) formattedHtmlMessage: '' // 带格式的HTML内容 }; // 递归查找指定MIME类型的邮件部分 const findMessagePart = (parts, targetMimeType) => { for (const part of parts) { if (part.mimeType === targetMimeType) { return part; } // 处理嵌套的parts结构 if (part.parts && part.parts.length > 0) { const nestedPart = findMessagePart(part.parts, targetMimeType); if (nestedPart) return nestedPart; } } return null; }; // 处理HTML内容 const htmlPart = findMessagePart([message.data.payload], 'text/html'); if (htmlPart) { let htmlContent = Buffer.from(htmlPart.body.data, 'base64').toString('utf-8'); // 替换内嵌图片的CID引用为Data URI const attachments = message.data.payload.parts?.filter(part => part.filename && part.body?.attachmentId && part.headers?.some(h => h.name === 'Content-ID') ) || []; attachments.forEach(attachment => { const cidHeader = attachment.headers.find(h => h.name === 'Content-ID'); const cid = cidHeader.value.replace(/[<>]/g, ''); // 去掉CID的<>包裹 const attachmentBase64 = Buffer.from(attachment.body.data, 'base64').toString('base64'); htmlContent = htmlContent.replace( `cid:${cid}`, `data:${attachment.mimeType};base64,${attachmentBase64}` ); }); // 净化HTML,防止XSS攻击 email.formattedHtmlMessage = purify.sanitize(htmlContent); } // 处理纯文本内容(作为HTML内容的降级备选) const textPart = findMessagePart([message.data.payload], 'text/plain'); if (textPart) { email.plainTextMessage = Buffer.from(textPart.body.data, 'base64').toString('utf-8'); }
关键说明
- 递归查找邮件部分:邮件结构可能嵌套多层(比如
multipart/related包含HTML和附件),递归遍历能确保找到目标MIME类型的内容。 - CID图片处理:将邮件附件转成Data URI嵌入HTML,无需额外请求即可渲染图片。
- HTML净化:DOMPurify会过滤掉
<script>、<iframe>等危险标签,保证渲染安全。
内容的提问来源于stack exchange,提问作者Suliman Khan
相关产品推荐
相关产品推荐

