Reputation: 47804
I am sending a mail using C# using the SmtpClient
class. I am doing the following things before sending the mail.
var mailMessage = new MailMessage();
model.ToAddresses.ForEach(to => mailMessage.To.Add(to));
mailMessage.Subject = "Test Email - By Yasser";
mailMessage.Body = String.Format("{0}{1}{2}",
"<html><body>",
GetEmailContent(model),
"</body></html>");
mailMessage.IsBodyHtml = true;
return MailService.SendEmail(mailMessage);
and below is my MailService class:
public class MailService
{
public static bool SendEmail(MailMessage mailMessage)
{
var smtpClient = new SmtpClient();
try
{
smtpClient.Send(mailMessage);
return true;
}
catch(Exception exp)
{
return false;
}
}
}
Now when I send the mail, the mail gets sent, here is what I get as the content of the mail in outlook when I press the view source. Below is the content of the email with view source (Obviously I have kept only a part of the image data)
<html>
<body>
<h1>Test</h1>
<h2>Hello World</h2>
<h3>Missing close h3 tag</h3>
<p>
<a href="www.google.com">
<img src="data:image/gif;base64,/9j/4AAQSkZJRgABAgEAYABgAAD/4Q8HRXhpZgAAT" />
</a>
</p>
</body>
</html>
So this appears broken(the images) in the mail, but when I copy this source and paste it into an editor and open the file using a browser all seems good (even the images).
Update : Added image of the mail from outlook
Any ideas ????
Upvotes: 8
Views: 9533
Reputation: 11
To go along with Igor's Visual Basic Response, here is it converted to C#
static void ProcessEmbeddingImages(ref MailMessage oMail)
{
Hashtable oLinkedResources = new Hashtable();
oMail.Body = PadSrcDataImage(oMail.Body, ref oLinkedResources);
if (oLinkedResources.Count > 0)
{
AlternateView oAlternateView = AlternateView.CreateAlternateViewFromString(oMail.Body, null, "text/html");
oAlternateView.ContentId = "htmlView";
oAlternateView.TransferEncoding = System.Net.Mime.TransferEncoding.SevenBit;
foreach (DictionaryEntry oItem in oLinkedResources)
{
string[] oKey = ((string)oItem.Key).Split('-');
int i = int.Parse(oKey[0]);
string sExt = oKey[1];
string sData = (string)oItem.Value;
LinkedResource oLinkedResource = new LinkedResource(new System.IO.MemoryStream(Convert.FromBase64String(sData)));
oLinkedResource.ContentId = "MyImg" + i;
oLinkedResource.TransferEncoding = System.Net.Mime.TransferEncoding.Base64;
oLinkedResource.ContentType = new System.Net.Mime.ContentType("image/" + sExt);
oAlternateView.LinkedResources.Add(oLinkedResource);
}
oMail.AlternateViews.Add(oAlternateView);
}
}
static string PadSrcDataImage(string sHtml, ref Hashtable oLinkedResources)
{
ArrayList oList = new ArrayList();
string sSearch = @"\ssrc=['""]data:image/(.*?);base64,(.*?)['""]";
MatchCollection oMatches = Regex.Matches(sHtml, sSearch, RegexOptions.IgnoreCase);
foreach (Match m in oMatches)
{
if (m.Groups.Count >= 2)
{
string sExt = m.Groups[1].Value;
string sData = m.Groups[2].Value;
if (sData.Length > 7 && sData.EndsWith("%3D%3D"))
{
// Replace trailing %3D%3D with ==
sData = sData.Substring(0, sData.Length - 6) + "==";
}
oLinkedResources.Add(oLinkedResources.Count + "-" + sExt, sData);
int iPos1 = m.Groups[1].Index - "data:image/".Length;
int iPos2 = m.Groups[2].Index + m.Groups[2].Length;
int[] iPoints = { iPos1, iPos2 };
oList.Add(iPoints);
}
}
string sRet = "";
if (oList.Count == 1) // One img
{
int[] iPoints = (int[])oList[0];
string sStr1 = sHtml.Substring(0, iPoints[0]);
string sStr2 = sHtml.Substring(iPoints[1]);
sRet = sStr1 + "cid:MyImg0" + sStr2;
}
else if (oList.Count > 1)
{
for (int i = 0; i < oList.Count; i++)
{
int[] iPoints = (int[])oList[i];
int iPos1 = iPoints[0];
if (i == 0)
{
// First img
string sStr1 = sHtml.Substring(0, iPos1);
sRet += sStr1 + "cid:MyImg" + i;
}
else // Rest imgs
{
int iPrevPos2 = ((int[])oList[i - 1])[1];
string sStr1 = sHtml.Substring(iPrevPos2, iPos1 - iPrevPos2);
sRet += sStr1 + "cid:MyImg" + i;
if (i == oList.Count - 1) // Last
{
sRet += sHtml.Substring(iPoints[1]);
}
}
}
}
if (!string.IsNullOrEmpty(sRet))
{
return sRet;
}
else
{
return sHtml;
}
}
Upvotes: 0
Reputation: 885
Here is a function that will fix the problem. Call it before you SendEmail like:
ProcessEmbeddingImages(mailMessage);
return MailService.SendEmail(mailMessage);
The ProcessEmbeddingImages function is written in VB.Net so you might want to translate it to c# using one of those online translators.
Private Sub ProcessEmbeddingImages(ByRef oMail As System.Net.Mail.MailMessage)
Dim oLinkedResources As New Hashtable()
oMail.Body = PadSrcDataImage(oMail.Body, oLinkedResources)
If oLinkedResources.Count > 0 Then
Dim oAlternateView As System.Net.Mail.AlternateView = System.Net.Mail.AlternateView.CreateAlternateViewFromString(oMail.Body, Nothing, "text/html")
oAlternateView.ContentId = "htmlView"
oAlternateView.TransferEncoding = Net.Mime.TransferEncoding.SevenBit
For Each oItem As DictionaryEntry In oLinkedResources
Dim oKey As String() = Split(oItem.Key, "-")
Dim i As Integer = oKey(0)
Dim sExt As String = oKey(1)
Dim sData As String = oItem.Value
Dim oLinkedResource As New System.Net.Mail.LinkedResource(New IO.MemoryStream(System.Convert.FromBase64String(sData)))
oLinkedResource.ContentId = "MyImg" & i
oLinkedResource.TransferEncoding = Net.Mime.TransferEncoding.Base64
oLinkedResource.ContentType = New System.Net.Mime.ContentType("image/" & sExt)
oAlternateView.LinkedResources.Add(oLinkedResource)
Next
oMail.AlternateViews.Add(oAlternateView)
End If
End Sub
Private Function PadSrcDataImage(ByVal sHtml As String, ByRef oLinkedResources As Hashtable) As String
Dim oList As New ArrayList
Dim sSearch As String = "\ssrc=['""]data:image/(.*?);base64,(.*?)['""]"
Dim oMatches As System.Text.RegularExpressions.MatchCollection = System.Text.RegularExpressions.Regex.Matches(sHtml, sSearch, System.Text.RegularExpressions.RegexOptions.IgnoreCase)
For Each m As System.Text.RegularExpressions.Match In oMatches
If m.Groups.Count >= 2 Then
Dim sExt As String = m.Groups(1).Value
Dim sData As String = m.Groups(2).Value
If sData.Length > 7 AndAlso Right(sData, 6) = "%3D%3D" Then
'Replace trailing %3D%3D with ==
sData = Left(sData, sData.Length - 6) & "=="
End If
oLinkedResources.Add(oLinkedResources.Count & "-" & sExt, sData)
Dim iPos1 As Integer = m.Groups(1).Index - "data:image/".Length
Dim iPos2 As Integer = m.Groups(2).Index + m.Groups(2).Length
Dim iPoints As Integer() = {iPos1, iPos2}
oList.Add(iPoints)
End If
Next
Dim sRet As String = ""
If oList.Count = 1 Then 'One img
Dim iPoints As Integer() = oList(0)
Dim sStr1 As String = sHtml.Substring(0, iPoints(0))
Dim sStr2 As String = sHtml.Substring(iPoints(1))
sRet = sStr1 & "cid:MyImg0" & sStr2
ElseIf oList.Count > 1 Then
For i As Integer = 0 To oList.Count - 1
Dim iPoints As Integer() = oList(i)
Dim iPos1 As Integer = iPoints(0)
If i = 0 Then
'First img
Dim sStr1 As String = sHtml.Substring(0, iPos1)
sRet += sStr1 & "cid:MyImg" & i
Else 'Rest imgs
Dim iPrevPos2 As Integer = oList(i - 1)(1)
Dim sStr1 As String = sHtml.Substring(iPrevPos2, iPos1 - iPrevPos2)
sRet += sStr1 & "cid:MyImg" & i
If i = oList.Count - 1 Then 'Last
sRet += sHtml.Substring(iPoints(1))
End If
End If
Next
End If
If sRet <> "" Then
Return sRet
Else
Return sHtml
End If
End Function
Upvotes: 3
Reputation: 47804
This is what I tried and works for me, tested in outlook, thunderbird and gmail. WORKS FINE !
You might want to check out the following resources I referred to make this happen :
Sample Code :
// we need to use the prefix 'cid' in the img src value
string emailReadyHtml = string.empty;
emailReadyHtml += "<p>Hello World, below are two embedded images : </p>";
emailReadyHtml += "<img src=\"cid:yasser\" >";
emailReadyHtml += "<img src=\"cid:smile\" >";
MailMessage mailMessage = new MailMessage();
mailMessage.To.Add("[email protected]");
mailMessage.From = new MailAddress("[email protected]", "Info");
mailMessage.Subject = "Test Mail";
mailMessage.IsBodyHtml = true;
string image1Path = HttpContext.Current.Server.MapPath("~/Content/images/yasser.jpg");
byte[] image2Bytes = someArrayOfByte;
ContentType c = new ContentType("image/jpeg");
// create image resource from image path using LinkedResource class.
LinkedResource linkedResource1 = new LinkedResource(imagePath);
linkedResource1.ContentType = c ;
linkedResource1.ContentId = "yasser";
linkedResource1.TransferEncoding = TransferEncoding.Base64;
// the linked resource can be created from bytes also, which may be stored in database (which was my case)
LinkedResource linkedResource2 = new LinkedResource(new MemoryStream(image2Bytes));
linkedResource2.ContentType = c;
linkedResource2.ContentId = "smile";
linkedResource2.TransferEncoding = TransferEncoding.Base64;
AlternateView alternativeView = AlternateView.CreateAlternateViewFromString(emailReadyHtml, null, MediaTypeNames.Text.Html);
alternativeView.ContentId = "htmlView";
alternativeView.TransferEncoding = TransferEncoding.SevenBit;
alternativeView.LinkedResources.Add(linkedResource1) ;
alternativeView.LinkedResources.Add(linkedResource2);
mailMessage.AlternateViews.Add(alternativeView);
SmtpClient smtpClient = new SmtpClient();
smtpClient.Send(mailMessage);
Upvotes: 16