Working with message attachments in Exchange Online
By George Durzi
Summary
This article explains how to use the EWS Managed API with Microsoft Exchange Online to create and work with email attachments.
Applies to: Microsoft Exchange Online │ Microsoft Exchange Web Services (EWS) Managed API
Published: April 2011
Introduction
The EWS Managed API includes functionality that enables you to work with attachments in email messages. You can integrate this functionality into your applications to streamline the process of creating and manipulating email message attachments. For example, if company employees email scans of their expense receipts to a dedicated email alias, you can create an application that extracts the receipt attachments from the email messages and associates them with a database record in a time and expense management application. You can create a customer relationship management (CRM) application that creates weekly tasks for account managers. The application can generate a Microsoft Office Outlook task mailbox item and attach it to an email message that is sent to the account managers.
In this article, I explain how you can use the message attachment capabilities in the EWS Managed API to work with files and mailbox item attachments in email messages.
Creating file attachments
You can attach a file to an email message by doing any of the following:
-
Pointing to a file on the local file system.
-
Generating the file attachment from a byte array.
-
Generating the file attachment from a file stream.
The method you choose will depend on the specific requirements of your application. If the file that has to be attached to the email message already exists, you can simply point to it on the local file system. If the file will be generated at runtime, you can create the attachment from a byte array or a file stream. All these methods enable you to choose the name of the file attachment that the recipient will see in the email message.
Creating a file attachment from an existing file
To attach an existing file to an email message, first create a new EmailMessage object and set its subject, body, and recipients. You can then add a file attachment to the Attachments collection of the message by calling its AddFileAttachment method and specifying the path to the file to attach, as shown in the following example. If the file does not exist at the specified path, an exception is thrown.
EmailMessage emailMessage = new EmailMessage(_exchangeService); emailMessage.Subject = "AdventureWorks Invoice"; emailMessage.Body = "Please review and approve the attached invoice."; emailMessage.ToRecipients.Add("User1@contoso.com"); emailMessage.Attachments.AddFileAttachment("C:\\Invoice.docx"); emailMessage.Send();
You can change the name of the file attachment that the recipient will see by using an overload of the AddFileAttachment method that enables you to specify a different name for the file attachment, as shown in the following example.
emailMessage.Attachments.AddFileAttachment("AdventureWorksInvoice.docx", "C:\\Invoice.docx");
Creating a file attachment from a byte array
You can also add a file attachment to an email message by using a byte array. This is useful when the file that needs to be attached to the email message doesn’t already exist on the file system, or when a unique copy of the file needs to be generated at runtime for each recipient.
You can generate a byte array by reading the contents of a file by using the File.ReadAllBytes method, or by encoding a string by using the various text encoding mechanisms that are available in the System.Text namespace. The following example shows how to use the GetBytes method of the System.Text.UTF8Encoding class to generate a byte array from a string.
// Generate a string that represents the file attachment contents. string expenseReport = GenerateExpenseReport("User1@contoso.com"); EmailMessage emailMessage = new EmailMessage(_exchangeService); emailMessage.Subject = "Expense Report"; emailMessage.Body = "Please review the attached expense report."; emailMessage.ToRecipients.Add("User1@contoso.com"); byte[] fileContents; UTF8Encoding encoding = new System.Text.UTF8Encoding(); fileContents = encoding.GetBytes(expenseReport); emailMessage.Attachments.AddFileAttachment("Expenses.txt", fileContents); emailMessage.Send();
Creating a file attachment from a file stream
The ability to create a file attachment from a stream is useful when the file you need to attach to the email message is stored in a database — for example, in a Microsoft SQL Server database that contains a table with a varbinary(max) column that has been configured for FILESTREAM storage.
The method used to create a stream for the file attachment depends on where the file itself is stored. The following example shows how to use a stream to read a file from a SQL Server table that is used to store sales report chart images in a varbinary(max) column.
SqlConnection sqlConnection = new SqlConnection("Integrated Security=true;server=(local)"); SqlCommand sqlCommand = new SqlCommand(); sqlCommand.Connection = sqlConnection; SqlFileStream sqlFileStream = null; try { sqlConnection.Open(); sqlCommand.CommandText = "SELECT Chart.PathName() " + " FROM CRM.dbo.SalesReports " + " WHERE Client = 'AdventureWorks'"; // Get the path of the SQL FILESTREAM BLOB. string filePath = null; object pathObj = sqlCommand.ExecuteScalar(); if (DBNull.Value != pathObj) { filePath = (string) pathObj; // FILESTREAM BLOB operations must occur in a transaction. SqlTransaction sqlTransaction = sqlConnection.BeginTransaction(); sqlCommand.Transaction = sqlTransaction; // Get the transaction context. sqlCommand.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()"; object fileStreamContext = sqlCommand.ExecuteScalar(); byte[] transactionContext = (byte[]) fileStreamContext; // Create a SqlFileStream for the FILESTREAM BLOB. using (sqlFileStream = new SqlFileStream( filePath, transactionContext, FileAccess.Read)) { EmailMessage emailMessage = new EmailMessage(_exchangeService); emailMessage.Subject = "AdventureWorks Sales Chart Year-to-Data"; emailMessage.Body = "Please review the attached sales chart."; emailMessage.ToRecipients.Add("User1@contoso.com"); // Attach the stream to the email message. emailMessage.Attachments.AddFileAttachment( "AdventureWorks_SalesYTDChart.png", sqlFileStream); emailMessage.Send(); } // Commit the transaction. sqlCommand.Transaction.Commit(); } } catch (Exception ex) { throw ex; } finally { sqlFileStream.Close(); }
Creating mailbox item attachments
In addition to attaching files to email messages, you can also use the EWS Managed API to attach Outlook mailbox items such as other email messages, contacts, calendar appointments, and tasks to email messages.
To attach a mailbox item to an email message, call the AddItemAttachment<T> method of the message’s Attachments collection; this returns an object of type ItemAttachment<T>. After you create the item attachment, set its Name property to the name for the attachment that will appear in the email message.
You can then access and set the individual properties of the mailbox item attachment by using the Item property of the ItemAttachment<T> object. For example, for a Task object attachment, you can set the status of the task attachment by using the following code: taskAttachment.Item.Status = TaskStatus.InProgress.
The following example shows how to create an email message that includes a task attachment.
EmailMessage emailMessage = new EmailMessage(_exchangeService); emailMessage.Subject = "Expense Report"; emailMessage.Body = "Please review the attached expense report for this pay period."; emailMessage.ToRecipients.Add("User1@contoso.com"); // Create the item attachment. ItemAttachment<Task> taskAttachment = emailMessage.Attachments.AddItemAttachment<Task>(); taskAttachment.Name = "Expense Report Approval Task"; taskAttachment.Item.Subject = "Review and Approve Expense Report"; taskAttachment.Item.DueDate = DateTime.Today.AddDays(7); // You can attach files and mailbox items to the same email message. emailMessage.Attachments.AddFileAttachment("ExpenseReport.xlsx"); emailMessage.Send();
Manipulating message attachments
You can use the EWS Managed API to interact with message attachments in a mailbox to download them or even remove them from a message. For example, Contoso employees send email scans of their expense receipts to a dedicated email alias. An application monitors the mailbox that is associated with the alias and downloads any receipt attachments for further processing.
Iterating Through Attachments in an Email Message
You can determine whether an individual EmailMessage object has any attachments by querying its HasAttachments property. However, if you need to access the attachments in the message, you have to bind to the specific message and request its attachments collection. If you don’t explicitly do this, you may see inconsistent behavior, such as the HasAttachments property set to "true" when the Attachments collection is empty.
After you bind to an email message using its unique ID, you can iterate through its Attachments collection to examine the individual attachments. An individual attachment is either a FileAttachment object or an ItemAttachment object. You can use the C# is operator to determine whether the attachment is of a particular type.
You can load an attachment into memory by casting to the appropriate attachment type and calling the Load method with no parameter. You are then able to access the properties of the attachment, such as Name and Size. In the case of an item attachment, you can access the underlying properties of the specific mailbox item type.
You can access some base properties of the attachment by explicitly using the Load method to load the attachment into memory. Later in this section I describe several methods that you can use to download file attachments by using overloads of the Load method.
Note: |
|---|
|
When you are working with an ItemAttachment object, the ItemAttachment.Item.ItemClass property represents the mailbox item type of the item — for example, IPM.Task for a task item. The following example shows how to iterate through the first 100 items in the user’s Inbox folder and check for attachments in each email message. |
ItemView itemView = new ItemView(100); itemView.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties); itemView.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending); FindItemsResults<Item> findResults = _exchangeService.FindItems( WellKnownFolderName.Inbox, itemView); foreach (Item item in findResults.Items) { if (item is EmailMessage && item.HasAttachments) { EmailMessage message = EmailMessage.Bind( _exchangeService, item.Id, new PropertySet(BasePropertySet.IdOnly, ItemSchema.Attachments)); Console.WriteLine("Item {0} has {1} attachment(s):", message.Id, message.Attachments.Count); foreach (Attachment attachment in message.Attachments) { if (attachment is FileAttachment) { FileAttachment fileAttachment = attachment as FileAttachment; fileAttachment.Load(); Console.WriteLine("File attachment: {0} ({1})", fileAttachment.Name, fileAttachment.Size); } else if (attachment is ItemAttachment) { ItemAttachment itemAttachment = attachment as ItemAttachment; itemAttachment.Load(); Console.WriteLine("{0} attachment: {1}", itemAttachment.Item.ItemClass, itemAttachment.Item.Subject); } } } }
Downloading file attachments
When you have a handle to a file attachment in an email message, you can easily download the file to disk or into a file stream by using the appropriate overload of the Load method.
To download a file attachment to disk, just specify the path you want to download the file to, as shown in the following example.
fileAttachment.Load("C:\\" + fileAttachment.Name);
You can also load a file attachment into a stream. You can use the contents of the stream to create another file or to write the file contents into a SQL Server database.
using (FileStream fileStream = new FileStream("C:\\" + fileAttachment.Name, FileMode.Create, FileAccess.ReadWrite)) { fileAttachment.Load(fileStream); // Do something with the stream. }
Deleting attachments from a message
You can also permanently remove an individual attachment or all attachments from an email message. To remove an individual attachment, use the Remove method of the message’s Attachments collection, as shown in the following example.
message.Attachments.Remove(attachment); message.Update(ConflictResolutionMode.AlwaysOverwrite);
For the changes to the message to take effect, call the Update method and specify a conflict resolution mode.
You can also clear the message’s entire Attachments collection, as shown in the following example.
message.Attachments.Clear(); message.Update(ConflictResolutionMode.AlwaysOverwrite);
Conclusion
The EWS Managed API provides functionality that enables you to create and manipulate message attachments easily in Exchange Online. You can create file attachments by pointing directly to a file, or by providing a byte array or file stream with the contents of the file to be attached to the message. You can also easily attach Outlook items such as email messages, tasks, appointments, and contacts to email messages. When working with existing email messages in an Exchange Online folder, you can determine whether a message has any attachments, and if it does, you can download the attachments, query their properties, or even delete them from the message.
About the Author
George Durzi is a Principal Consultant at Clarity Consulting, where he works with clients to implement solutions based on various Microsoft tools and technologies. George started working with Office 365 as part of a project for the Microsoft Developer and Platform Evangelism team to build and deliver developer training content for early adopters of SharePoint Online, Lync Online, and Exchange Online. George is also the co-author of Professional Unified Communications Development with Microsoft Lync Server 2010.
Note: