When you open an MSG file with Aspose.Email, every attachment is read into memory by default. That is wasteful if you only need the subject, the recipients, or one PDF out of a dozen large files. Aspose.Email for .NET adds deferred attachment loading, which leaves attachment content in the file until you actually ask for it. In this article, you will learn how to load MSG attachments on demand to reduce memory use in C#.

This guide is for .NET developers who process MSG files, such as archive tools, compliance scanners, or services that receive uploaded messages. You will see three common scenarios and the rules to follow while attachment content is read on demand.

How deferred loading works

With deferred loading turned on, Aspose.Email reads the message itself and the list of attachments, including their names and sizes. The content of each attachment stays in the source until you open it or read its data. Memory use then depends on what you open, not on how large the attachments are.

You turn the feature on through the load options:

var options = new MsgLoadOptions { DeferAttachmentContent = true };

A few details to know:

  • The option is off by default, so existing code behaves as before.
  • Small attachments, under 4096 bytes by default, are still loaded right away. You can change that limit with the DeferralThreshold option.
  • Only regular file attachments are deferred. OLE objects and messages attached to other messages always load with the message.

Prerequisites

Install Aspose.Email for .NET from NuGet

Install-Package Aspose.Email

The examples use the Aspose.Email, Aspose.Email.Mapi, and System.IO namespaces, and the options object shown above.

List attachments without loading them

To build an index or a report, you usually need only the message details and a list of what is attached. The following code prints the subject, the number of recipients, and each attachment’s name and size. No attachment content is read.

using (var msg = MapiMessage.Load(@"C:\data\large.msg", options))
{
    Console.WriteLine($"Subject: {msg.Subject}");
    Console.WriteLine($"Recipients: {msg.Recipients.Count}");
    foreach (MapiAttachment attachment in msg.Attachments)
    {
        Console.WriteLine($"  {attachment.LongFileName}: {attachment.ContentLength} bytes");
    }
}

Because the sizes are available up front, you can decide what to extract before you extract anything.

Extract only the attachments you need

The next example saves only the PDF attachments. Aspose.Email reads the content of those PDFs and leaves the other attachments untouched in the file.

using (var msg = MapiMessage.Load(@"C:\data\large.msg", options))
{
    foreach (MapiAttachment attachment in msg.Attachments)
    {
        if (!attachment.LongFileName.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
        {
            continue;
        }
        using (Stream source = attachment.OpenRead())
        using (Stream target = File.Create(Path.Combine(@"C:\out", attachment.LongFileName)))
        {
            source.CopyTo(target);
        }
    }
}

After the code runs, the PDFs from large.msg are in C:\out. Each attachment is copied as a stream, so it is never held in memory as a whole. You can filter by size in the same way, for example to skip anything above a limit.

Load a message from a stream

Deferred loading also works when the MSG file arrives as a stream, which is common in web services that accept uploads.

using (var source = File.OpenRead(@"C:\data\large.msg"))
{
    var msg = MapiMessage.Load(source, options);
    var data = msg.Attachments[0].BinaryData;
    Console.WriteLine($"Read {data.Length} bytes");
}

Only the first attachment is read here. Reading an attachment’s data this way loads it into memory in full, which is fine for small files. For large ones, open the attachment as a stream, as in the previous example.

Keep the source open while you use the message

Because attachment content stays in the source, the source has to stay available for as long as you use the message:

  • When you load from a file path, the message keeps the file open and closes it when the message is disposed. Always dispose the message, or the file stays locked.
  • When you load from a stream, the stream stays yours. Keep it open until you are finished with the message, then close it yourself.

If you read an attachment after its source has been closed, Aspose.Email throws an ObjectDisposedException. You get a clear error rather than partial or empty data.

You can save the message back over the file it came from. Aspose.Email reads the remaining attachment content, releases the original file, and then writes the new one. After that save, the old source is gone, so load the file again if you need to keep working with it.

Next steps

Deferred loading works well with the stream-based attachment support. See Add Large Attachments with Stream Support in C# to create MSG files with large attachments. If you have questions, ask on the Aspose.Email support forum.

FAQs

  1. What does deferred attachment loading do? It leaves attachment content in the source file until you ask for it. The message and the list of attachments load as usual, but the attachment data is read only when you open or access an attachment. It is off by default.

  2. Are all attachments deferred? No. Attachments smaller than 4096 bytes are loaded right away. You can change this limit with the DeferralThreshold option. OLE objects and messages attached to other messages are always loaded with the message.

  3. Can I see attachment names and sizes without loading them? Yes. Names and sizes are available without reading the attachment content.

  4. Why is my MSG file still locked after processing? When deferred loading is on, the message keeps the file open so it can read attachments later. The file is released when you dispose the message.

  5. What happens if I close the source too early? Reading an attachment after its source has been closed throws an ObjectDisposedException. You do not get partial or empty data.

Get a Free License and Explore More

Request a free temporary license to test Aspose.Email for .NET without evaluation restrictions.