新Outlook Web加载项:如何将选中邮件以文件附件形式转发至指定邮箱
问题
开发新Outlook Web加载项时,需要将当前选中邮件(含所有附件)发送至指定邮箱。使用Yeoman Office生成器创建项目,通过Office.context.mailbox.displayNewMessageForm实现邮件发送,但附件处理遇到问题:
- 附件类型设为
file时无法正常加载附件 - 附件类型设为
item时,附件显示为Outlook项目,打开方式与原Outlook项目一致
用户代码如下:
const subject = 'Test Subject'; const item = Office.context.mailbox.item; console.log(item); const itemId = Office.context.mailbox.item.itemId; // Separate inline and regular attachments const inlineAttachments = attachments.filter(attachment => attachment.isInline); const regularAttachments = attachments.filter(attachment => !attachment.isInline); // Convert the attachment IDs to REST IDs const inlineRestIds = inlineAttachments.map(attachment => Office.context.mailbox.convertToRestId(attachment.id, Office.MailboxEnums.RestVersion.v2_0) ); const regularRestIds = regularAttachments.map(attachment => Office.context.mailbox.convertToRestId(attachment.id, Office.MailboxEnums.RestVersion.v2_0) ); // Get the email subject and body const originalSubject = item.subject; item.body.getAsync("html", (bodyResult) => { if (bodyResult.status === Office.AsyncResultStatus.Succeeded) { const originalBody = bodyResult.value; const emailHtmlBody = ` <p><strong>Original Subject:</strong> ${originalSubject}</p> <p><strong>Original Body:</strong></p> ${originalBody} `; // Open a pre-filled compose window Office.context.mailbox.displayNewMessageForm({ to: ["specific.email@example.com"], subject: subject, htmlBody: emailHtmlBody, attachments: [ ...inlineRestIds.map((restId, index) => ({ type: 'file', name: inlineAttachments[index].name, url: restId, isInline: true })), ...regularRestIds.map((restId, index) => ({ type: 'file', name: regularAttachments[index].name, url: restId })) ] }); } else { console.error("Failed to get body:", bodyResult.error.message); } });
解决方案
要实现将原邮件附件以普通文件形式添加到新邮件中,需通过Outlook REST API获取附件二进制内容并转换为Base64格式,再传递给displayNewMessageForm,具体步骤如下:
1. 获取访问令牌与附件二进制内容
首先获取REST API访问令牌,再通过API拉取附件的二进制数据并转换为Base64字符串:
// 获取REST访问令牌 Office.context.mailbox.getCallbackTokenAsync({ isRest: true }, (tokenResult) => { if (tokenResult.status !== Office.AsyncResultStatus.Succeeded) { console.error("获取令牌失败:", tokenResult.error.message); return; } const accessToken = tokenResult.value; const messageRestId = Office.context.mailbox.convertToRestId(itemId, Office.MailboxEnums.RestVersion.v2_0); // 批量处理所有附件 const processAttachments = (attachmentsList, isInline) => { return Promise.all(attachmentsList.map(attachment => { const attachmentRestId = Office.context.mailbox.convertToRestId(attachment.id, Office.MailboxEnums.RestVersion.v2_0); const apiUrl = `https://outlook.office.com/api/v2.0/me/messages/${messageRestId}/attachments/${attachmentRestId}/$value`; return fetch(apiUrl, { headers: { 'Authorization': `Bearer ${accessToken}` } }) .then(res => res.blob()) .then(blob => { return new Promise((resolve) => { const reader = new FileReader(); reader.onloadend = () => { resolve({ name: attachment.name, base64Content: reader.result.split(',')[1], // 提取Base64编码部分 isInline: isInline, contentId: attachment.contentId // 保留内嵌附件的Content-ID }); }; reader.readAsDataURL(blob); }); }); })); }; // 同时处理内嵌和普通附件 Promise.all([ processAttachments(inlineAttachments, true), processAttachments(regularAttachments, false) ]).then(([processedInline, processedRegular]) => { // 后续调用displayNewMessageForm renderNewMessageForm([...processedInline, ...processedRegular]); }).catch(err => console.error("处理附件失败:", err)); });
2. 调用displayNewMessageForm添加Base64附件
将处理好的Base64附件传入displayNewMessageForm,注意file类型附件需使用base64Content字段:
function renderNewMessageForm(processedAttachments) { item.body.getAsync("html", (bodyResult) => { if (bodyResult.status !== Office.AsyncResultStatus.Succeeded) { console.error("获取邮件正文失败:", bodyResult.error.message); return; } const originalBody = bodyResult.value; const emailHtmlBody = ` <p><strong>Original Subject:</strong> ${item.subject}</p> <p><strong>Original Body:</strong></p> ${originalBody} `; Office.context.mailbox.displayNewMessageForm({ to: ["specific.email@example.com"], subject: 'Test Subject', htmlBody: emailHtmlBody, attachments: processedAttachments.map(att => ({ type: 'file', name: att.name, base64Content: att.base64Content, isInline: att.isInline, contentId: att.contentId // 内嵌附件需要此ID关联正文图片 })) }); }); }
关键注意事项
- 权限配置:确保加载项manifest.xml中声明
ReadWriteMailbox权限,否则无法访问REST API获取附件 - 异步处理:使用
Promise.all确保所有附件处理完成后再调用displayNewMessageForm - 内嵌附件:必须保留原附件的
contentId,否则正文中的内嵌图片无法正常显示
内容的提问来源于stack exchange,提问作者avnish maddheshiya




