-
-
Save DevDuck/db4e7e5903d5d3d2d49a238871da4311 to your computer and use it in GitHub Desktop.
| import { Buffer } from "buffer"; | |
| var item: Office.MessageRead; | |
| Office.onReady(() => { | |
| item = Office.context.mailbox.item; | |
| }); | |
| /** | |
| * Gets the email in EML format (IMF text file). This function executes the following steps: | |
| * (1) item.getAllInternetHeadersAsync() | |
| * (2) extract MIME boundary from headers to separate messages | |
| * (3) item.body.getAsync(Office.CoercionType.Html) & base64 encode | |
| * (4) item.body.getAsync(Office.CoercionType.Text) & base64 encode | |
| * (5) iterate through item.attachments & item.getAttachmentContentAsync() | |
| * You can do what you want with the file (string) once complete. | |
| * Note that this code does not encode the attachments into the file. | |
| * @param options - An object literal that contains one or more of the following properties:- | |
| * `asyncContext`: Developers can provide any object they wish to access in the callback function. | |
| * @param callback - Optional. When the method completes, the function passed in the `callback` parameter is called with a single parameter, | |
| * `asyncResult`, which is an `Office.AsyncResult` object. | |
| * On success, the internet headers data is provided in the `asyncResult.value` property as a string. | |
| * Refer to {@link https://tools.ietf.org/html/rfc2183 | RFC 2183} for the formatting information of the returned string value. | |
| * If the call fails, the `asyncResult.error` property will contain an error code with the reason for the failure. | |
| */ | |
| export const getEmailContentAsync = async ( | |
| options: Office.AsyncContextOptions, | |
| callback?: (asyncResult: Office.AsyncResult<string>) => void | |
| ) => { | |
| await item.getAllInternetHeadersAsync( | |
| options, | |
| await callbackWrapper(options, callback, async (headerResult) => { | |
| let header = headerResult.value; | |
| if (!header) { | |
| let headers = [`From: ${item.from.displayName} <${item.from.emailAddress}>`]; | |
| if (item.to && item.to.length) | |
| headers.push(`To: ${item.to.map((e) => `${e.displayName} <${e.emailAddress}>`).join(", ")}`); | |
| if (item.cc && item.cc.length) | |
| headers.push(`CC: ${item.cc.map((e) => `${e.displayName} <${e.emailAddress}>`).join(", ")}`); | |
| headers.push(`Subject: ${item.subject}`); | |
| headers.push(`Date: ${item.dateTimeCreated.toUTCString()}`); | |
| headers.push(`Message-ID: ${item.internetMessageId}`); | |
| header = headers.join("\r\n"); | |
| } | |
| await item.body.getAsync( | |
| Office.CoercionType.Html, | |
| callbackWrapper(options, callback, async (htmlResult) => { | |
| await item.body.getAsync( | |
| Office.CoercionType.Text, | |
| callbackWrapper(options, callback, async (result) => { | |
| const boundaryMatches = header.match(/boundary="([\w'()+,-./:=?]+)"/); | |
| const alternativeMatches = header.match(/Content-Type: multipart\/alternative;/); | |
| let boundary: string; | |
| if (boundaryMatches) { | |
| boundary = boundaryMatches[1]; | |
| } else { | |
| boundary = `_${item.conversationId.substring(0, 68)}_`; | |
| header += `Content-Type: multipart/mixed;\r\n boundary="${boundary}"\r\n`; | |
| } | |
| let messageBoundary = "_body_" + boundary.slice(6); | |
| if (alternativeMatches) { | |
| // Can skip multipart/mixed content-type header | |
| messageBoundary = boundary; | |
| } | |
| const body = result.value; | |
| const bodyHtml = htmlResult.value; | |
| let content = `${header}MIME-Version: 1.0\r\n`; | |
| if (!alternativeMatches) { | |
| content += `\r\n--${boundary}\r\nContent-Type: multipart/alternative;\r\n boundary="${messageBoundary}"\r\n`; | |
| } | |
| content += `\r | |
| --${messageBoundary}\r | |
| Content-Type: text/plain; charset="utf-8"\r | |
| Content-Transfer-Encoding: base64\r | |
| \r | |
| ${base64Chunk(body).join("\r\n")}\r | |
| \r | |
| --${messageBoundary}\r | |
| Content-Type: text/html; charset="utf-8"\r | |
| Content-Transfer-Encoding: base64\r | |
| \r | |
| ${base64Chunk(bodyHtml).join("\r\n")}\r | |
| \r | |
| --${messageBoundary}--`; | |
| item.attachments.forEach((a) => { | |
| content += `\r\n\r\n--${boundary}\r | |
| Content-Type: ${a.contentType == "message/rfc822" ? "text/plain" : a.contentType}; name="${a.name}"\r | |
| Content-Description: ${a.name}\r | |
| Content-Disposition: ${a.isInline ? "inline" : "attachment"}; filename="${a.name}"; size=${a.size}`; | |
| // NB: This is where you would inline each attachment. I currently don't have need | |
| // to do this since I save attachments separately, but this is where you might do | |
| // item.getAttachmentContentAsync(a.id, options, ...); | |
| }); | |
| if (!alternativeMatches) content += `\r\n\r\n--${boundary}--`; | |
| callback({ | |
| asyncContext: options?.asyncContext, | |
| diagnostics: undefined, | |
| error: undefined, | |
| status: Office.AsyncResultStatus.Succeeded, | |
| value: content, | |
| }); | |
| }) | |
| ); | |
| }) | |
| ); | |
| }) | |
| ); | |
| }; | |
| // Base64 encode string and chunk into 76 char lines | |
| const base64Chunk = (str: string): string[] => { | |
| let encoded = Buffer.from(str).toString("base64"); | |
| let maxLength = 76; | |
| let result: string[] = []; | |
| while (encoded.length > maxLength) { | |
| result.push(encoded.slice(0, maxLength)); | |
| encoded = encoded.substring(maxLength); | |
| } | |
| result.push(encoded); | |
| return result; | |
| }; | |
| // Wrap callback in try/catch block | |
| const callbackWrapper = ( | |
| options: Office.AsyncContextOptions, | |
| callback?: (asyncResult: Office.AsyncResult<string>) => void, | |
| processSuccess?: (asyncResult: Office.AsyncResult<string>) => Promise<void> | |
| ) => { | |
| return async (asyncResult: Office.AsyncResult<string>) => { | |
| try { | |
| if (asyncResult.status != Office.AsyncResultStatus.Succeeded) { | |
| if (callback) callback(asyncResult); | |
| return; | |
| } | |
| if (processSuccess) await processSuccess(asyncResult); | |
| } catch (err) { | |
| if (callback) callback(wrapException(err, options.asyncContext)); | |
| } | |
| }; | |
| }; | |
| // Wrap exception in asyncResult with Office error | |
| const wrapException = (err, asyncContext?): Office.AsyncResult<any> => { | |
| let officeError = { | |
| code: 5001, // Internal error | |
| name: "getEmailContentAsync", | |
| message: undefined, | |
| }; | |
| let asyncResult = { | |
| asyncContext: asyncContext, | |
| diagnostics: err, | |
| error: officeError, | |
| status: Office.AsyncResultStatus.Failed, | |
| value: undefined, | |
| }; | |
| if (err instanceof Error) { | |
| officeError.name = err.name; | |
| officeError.message = err.message; | |
| } | |
| return asyncResult; | |
| }; |
It looks like Microsoft has created a new method for doing this with the native Javascript API:
https://learn.microsoft.com/en-us/javascript/api/outlook/office.messageread?view=outlook-js-preview&preserve-view=true#outlook-office-messageread-getasfileasync-member(1)
Hi prmoore77
I am new to outlook add in development , i have to save the mail as .email file as you have mentioned here i used the same approach and same code that you have mentioned like this
try {
await getEmailContentAsync(
{},
async (asyncResult: Office.AsyncResult) => {
if (asyncResult.status != Office.AsyncResultStatus.Succeeded) {
// handle error
return;
}
const content = asyncResult.value;
const xx = JSON.stringify(content);
console.log('content is :' + { xx });
// upload file
//Buffer.from(content, 'utf8');
}
);
} catch (err) {
console.log('Error in saving file');
}
What ever content i am geting from this code that i am saving as .eml file but on opening the saved .eml file body part is block can you pls help me on this pls
Regards
Rajesh
i also want to get the outlook email in .eml file and upload on the server i am using getAsFileAsync
here is my code
Office.context.mailbox.item.getAsFileAsync({
},function(asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Failed) {
return;
}
console.log(asyncResult.value);
});
it's not working
I wanted to document the way to call the code here (taken from @DevDuck's post to: https://techcommunity.microsoft.com/t5/microsoft-365-developer-platform/allow-msg-file-export-through-js-add-in-api-in-outlook-desktop/idc-p/3925786/highlight/true#M1969):
Thanks again for creating this!