Is it possible to read the Nth attachment of the Mth message, from an mbox file, using MimeKit.MimeParser? In my case, I would store few messages (few fields for each msg including a list of attachments) to an in-memory data structure and after that, I want to be able to return to a specific message attachment and read its contents.
Things I have tried so far:
The above does not work.
Here is some code just to illustrate my efforts:
public void SaveAttachment(Attachment att, Stream outStream)
{
_inputStream.Seek(0, SeekOrigin.Begin);
_parser.SetStream(_inputStream, false);
//MimeMessage mimeMsg = _parser.Skip((int)(att.Parent as Message).Position).First();
MimeMessage mimeMsg =_parser.SingleOrDefault(x => x.MessageId == (att.Parent as Message).EntryID);
MimeEntity mimeAtt = mimeMsg.Attachments.ToList()[att.AttachmentIndex];
if (mimeAtt is MessagePart)
{
(mimeAtt as MessagePart).Message.WriteTo(outStream);
}
else
{
(mimeAtt as MimePart).Content.DecodeTo(outStream);
}
}
Is it possible to read the Nth attachment of the Mth message, from an mbox file, using MimeKit.MimeParser?
If you want to do this, then you will need exact stream start/end offsets of the MimeEntity that you want.
Then what you'll want to do is to wrap the stream in a MimeKit.IO.BoundStream with those stream offsets to prevent the parser from straying outside of those bounds and set the BoundStream on the MimeParser.
When you set the stream, make sure to use MimeFormat.Entity
(and not MimeFormat.Mbox
) since you are only interested in parsing a single MimeEntity
(which can be a multipart containing other MimeEntities).
To get these offsets, you'll need to subscribe to the MimeParser's MimeEntityBegin/End events when you first parse the mbox: http://www.mimekit.net/docs/html/Events_T_MimeKit_MimeParser.htm
I want to be able to return to a specific message attachment and read its contents.
Have you looked into the persistent
argument to MimeParser.SetStream()?
This may still use more memory than you want to use (since it will have all of the headers loaded + track stream offsets for each MimeEntity's content), but you may find that it's more convenient and has low-enough memory usage to fit your practical needs.
When this property is set to true
, instead of loading each MimePart's content into RAM, it instead creates a BoundStream that wraps the stream provided to the MimeParser so that when you request the content of these MimeParts, it lazily loads it from disk.
By default (or when persistent = false
), the MimeParser will load that content into a MemoryBlockStream (effectively a MemoryStream that tries to reduce byte array resizing for performance) which can, as you probably know, use quite a bit of memory if the messages have large attachments (or a lot of them).
The thing to watch out for when using persistent = true
is that you will need to keep the mbox file stream open if you want to be able to get the content of any of the MimeParts parsed by the parser. Once you close the stream, trying to get the content of any MimeParts will likely result in an ObjectDisposedException.