如何将Gmail的multipart/related内容转为HTML或PDF?附API调用示例
嘿,我来帮你搞定这个Gmail API的邮件内容处理问题!结合你用gmail.users.threads.get({ format: 'full' })获取线程的代码,我分步骤给你拆解解决方案:
一、把Multipart/Related格式转成可用的HTML
Multipart/Related类型的邮件结构其实是把HTML内容和内嵌资源(比如图片、内嵌样式)打包在一起的,资源通过cid:前缀的链接引用。你可以按下面的步骤提取并处理:
定位并解码HTML主体部分
首先得从线程的消息里找到text/html类型的内容块,因为邮件可能嵌套多层parts,所以用递归查找更靠谱:// 从返回的线程数据里取第一条消息(一般就是主邮件) const targetMessage = full.data.messages[0]; const payload = targetMessage.payload; // 递归查找text/html类型的part function getHtmlPart(part) { if (part.mimeType === 'text/html') return part; if (part.parts) { for (const subPart of part.parts) { const found = getHtmlPart(subPart); if (found) return found; } } return null; } const htmlPart = getHtmlPart(payload); if (!htmlPart) { // fallback:如果没有HTML,把纯文本转成简单HTML const textPart = payload.parts.find(p => p.mimeType === 'text/plain'); if (textPart) { const plainText = Buffer.from(textPart.body.data, 'base64').toString('utf-8'); const html = plainText.replace(/\n/g, '<br>').replace(/\t/g, ' '); // 这里拿到的html可以直接用或者转PDF } return; } // 解码base64的HTML内容 let finalHtml = Buffer.from(htmlPart.body.data, 'base64').toString('utf-8');替换内嵌资源的CID引用
邮件里的图片都是用cid:xxx引用的,浏览器没法直接识别,所以我们把这些资源转成base64内嵌到HTML里:// 收集所有带content-id的内嵌资源(比如图片) const inlineAssets = []; function collectInlineParts(part) { if (part.mimeType.startsWith('image/') && part.headers) { const contentId = part.headers.find(h => h.name.toLowerCase() === 'content-id'); if (contentId) { // 去掉CID前后的<>符号 const cid = contentId.value.replace(/[<>]/g, ''); inlineAssets.push({ cid, base64Data: part.body.data, mimeType: part.mimeType }); } } if (part.parts) part.parts.forEach(collectInlineParts); } collectInlineParts(payload); // 把HTML里的cid:xxx替换成base64数据URL inlineAssets.forEach(asset => { const dataUrl = `data:${asset.mimeType};base64,${asset.base64Data}`; finalHtml = finalHtml.replace(new RegExp(`cid:${asset.cid}`, 'g'), dataUrl); }); // 现在finalHtml就是可以直接在浏览器打开的完整HTML了
二、把HTML转成PDF
要把HTML转成PDF,首推puppeteer——它基于Chrome无头浏览器,能完美渲染复杂的HTML和CSS,比轻量库靠谱得多:
先装依赖:
npm install puppeteer然后写转换代码:
const puppeteer = require('puppeteer'); const fs = require('fs').promises; async function convertHtmlToPdf(htmlContent) { const browser = await puppeteer.launch({ headless: 'new' }); const page = await browser.newPage(); await page.setContent(htmlContent, { waitUntil: 'networkidle0' }); // 等资源加载完 const pdfBuffer = await page.pdf({ format: 'A4', printBackground: true // 要渲染背景色的话开这个 }); await browser.close(); return pdfBuffer; } // 调用示例:把刚才的finalHtml转成PDF并保存 const pdf = await convertHtmlToPdf(finalHtml); await fs.writeFile('email_output.pdf', pdf);
如果你的邮件HTML很简单,也可以用html-pdf库,它更轻量,但复杂样式可能渲染崩,按需选择就行。
三、更简便的Gmail内容获取方式
你现在用format: 'full'需要手动解析multipart结构,其实有更省心的办法:
用第三方解析库自动处理:比如
gmail-api-parse-message或者mailparser,这些库能帮你跳过手动遍历parts的步骤,直接拿到处理好的HTML:
举个mailparser的例子,配合format: 'raw'使用:const { simpleParser } = require('mailparser'); // 用raw格式获取完整邮件内容 const rawThread = await gmail.users.threads.get({ userId, id: firstThread.id, format: 'raw' }); // 解码base64的原始邮件并解析 const decodedRaw = Buffer.from(rawThread.data.raw, 'base64').toString('utf-8'); const parsedEmail = await simpleParser(decodedRaw); // 直接拿到处理好的HTML(已经替换了内嵌资源) const readyHtml = parsedEmail.html;这种方式完全不用自己处理multipart结构,库会帮你搞定所有细节,非常适合快速开发。
如果只需要纯文本:可以用
format: 'minimal'或者format: 'metadata',但你要HTML/PDF的话,上面的raw+mailparser方案最省事。
内容的提问来源于stack exchange,提问作者Sibelius Seraphini

