Recursive AI

Create an EML file in C#

All onemail recipes

Build the message from MimeMultipart and MimePart, add the headers, and call WriteTo. The program below writes an email with plain text and HTML bodies and a CSV attachment, then reads it back.

Terminal
dotnet new console -o CreateEml && cd CreateEml
dotnet add package RecursiveAI.OneMail --version 0.2.0-preview.2

Put this in Program.cs and run dotnet run:

C#
using System.Globalization;
using System.Text;
using RecursiveAI.OneMail;
using RecursiveAI.OneMail.Mime;
using RecursiveAI.OneMail.Mime.Transfer;
using RecursiveAI.OneMail.Text;
 
var message = new MimeMultipart("mixed");
// Address headers are written as given: encode a non-ASCII display name yourself
message.Headers.Add("From", $"{Rfc2047.Encode("Zoë Martín")} <[email protected]>");
message.Headers.Add("To", "Sam Lee <[email protected]>");
// Free text such as the subject is encoded for you
message.Headers.Add("Subject", "Q3 report – final");
string date = DateTime.UtcNow.ToString("ddd, dd MMM yyyy HH:mm:ss +0000",
    CultureInfo.InvariantCulture);
message.Headers.Add("Date", date);
message.Headers.Add("Message-ID", $"<{Guid.NewGuid():N}@example.com>");
message.Headers.Add("MIME-Version", "1.0");
 
// Plain text and HTML versions of the same body
var body = new MimeMultipart("alternative");
var text = new MimePart("text/plain; charset=utf-8");
text.SetDecodedContent(Utf8("Hi Sam,\r\n\r\nThe report is attached.\r\n"),
    ContentTransferEncoding.QuotedPrintable);
var html = new MimePart("text/html; charset=utf-8");
html.SetDecodedContent(Utf8("<p>Hi Sam,</p><p>The report is attached.</p>"),
    ContentTransferEncoding.QuotedPrintable);
body.Add(text);
body.Add(html);
message.Add(body);
 
// An attachment, base64-encoded (the default)
var csv = new MimePart("text/csv; name=\"q3.csv\"");
csv.Headers.Add("Content-Disposition", "attachment; filename=\"q3.csv\"");
csv.SetDecodedContent(Utf8("month,revenue\r\nJul,1200\r\nAug,1350\r\n"));
message.Add(csv);
 
using (FileStream file = File.Create("report.eml"))
    message.WriteTo(file);
Console.WriteLine($"report.eml: {new FileInfo("report.eml").Length:N0} bytes");
 
// Read it back
IMailItem check = Mail.Parse(File.ReadAllBytes("report.eml"));
Console.WriteLine($"{check.From[0]} -> {check.ToRecipients[0]}: {check.Subject}");
string plain = check.TextBody!.ReadText();
Console.WriteLine($"text: {plain.ReplaceLineEndings(" ").Trim()}");
IMailAttachment attached = check.Attachments[0];
Console.WriteLine($"attachment: {attached.FileName}, {attached.Size} bytes");
Console.WriteLine($"defects: {check.Defects.Count()}");
 
static byte[] Utf8(string s) => Encoding.UTF8.GetBytes(s);
Text
report.eml: 1,019 bytes
Zoë Martín <[email protected]> -> Sam Lee <[email protected]>: Q3 report – final
text: Hi Sam,  The report is attached.
attachment: q3.csv, 35 bytes
defects: 0

report.eml opens in any mail program; Python’s email package also reads it with no defects. A boundary for each multipart is generated for you, and lines end in CRLF as MIME requires.

How the parts fit

  • multipart/mixed holds the body and the attachments.
  • multipart/alternative inside it holds versions of the same body, plainest first. Mail programs show the last one they support.
  • SetDecodedContent takes the raw bytes and encodes them: base64 by default, or quoted-printable for text people may read in the source.
  • Header values must be one line. A line break throws, so user input can’t inject headers.

Limits

  • A preview. 0.2.0-preview.2: the API can change before 1.0.
  • A low-level API. There is no message builder yet. You set each header yourself, and you encode non-ASCII display names in address headers with Rfc2047.Encode, as above. Subjects and other free text are encoded for you.
  • No sending. SMTP isn’t in this preview. The file is ready to hand to any SMTP client.
  • No Outlook formats. OneMail can’t write MSG or PST files yet.

Reading messages back: Parse an EML file. The writer: Reading and writing MIME.