How to attach a file as an attachment in email using SendGrid? - c#

I am wanting to attach a file as an attachment using SendGrid and C# - I have the code below which runs but the response.StatusCode that is returned is
BadResponse
How do I alter this code so that the file is attached and an email sent successfully?
var msg = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent);
var listAtta = new List<FileAttachment>();
emailProducts.Select(o => o.tp).ToList().ForEach(o =>
{
string file = o.ProductPdf;
var fileBytes = FileToByteArray(o.ProductPdf);
if (fileBytes != null && fileBytes.Count() > 0)
{
listAtta.Add(new FileAttachment
{
FileData = fileBytes,
FileName = o.ProductPdf
}); ;
}
msg.AddAttachment(o.ProductPdf, fileBytes.ToString());
});
var response = await client.SendEmailAsync(msg);
var success = response.StatusCode;

you need to base64 encode it
msg.AddAttachment(o.ProductPdf, Convert.ToBase64String(bytes) )

Related

Microsoft.Graph Forward as attachment

I'm using updating out Exchange Mail Client written in .NET 4 Framework using Exchange Web Services (EWS) to .NET Core 6 using Microsoft.Graph. I've managed to port most of the functions but I'm having trouble working out how to Forward a Message as an Attachment.
In EWS
// This method results in a GetItem call to EWS.
var msgToAttach = EWS.EmailMessage.Bind(
ServiceInstance,
message.Source.Id,
new EWS.PropertySet(EWS.ItemSchema.MimeContent, EWS.ItemSchema.Subject));
// Create an email message and set properties on the message.
var forward = new EWS.EmailMessage(ServiceInstance);
if (!bodyType.HasValue)
bodyType = message.BodyType;
forward.Subject = subject == null ? $"FW: {message.Subject}" : subject;
forward.Body = new EWS.MessageBody(
bodyType.Value == BodyTypes.Html ? EWS.BodyType.HTML : EWS.BodyType.Text,
body ?? "Please refer to the attachment message");
// Add additional recipients to the reply email message.
if (to?.Any() ?? false)
forward.ToRecipients.AddRange(to);
if (cc?.Any() ?? false)
forward.CcRecipients.AddRange(cc);
if (bcc?.Any() ?? false)
forward.BccRecipients.AddRange(bcc);
// before we can add the attachments, we need to save the draft
// according to the docoumentation this isn't required but without
// it throws an exception on Save/SendAndSaveCopy if the attachments
// are added to a message which doesn't exist
forward.Save();
// Add an email message item attachment and set properties on the item.
EWS.ItemAttachment<EWS.EmailMessage> itemAttachment = forward.Attachments.AddItemAttachment<EWS.EmailMessage>();
itemAttachment.Item.MimeContent = msgToAttach.MimeContent;
itemAttachment.Name = msgToAttach.Subject;
// Send the mail and save a copy in the Sent Items folder.
// This method results in a CreateItem and SendItem call to EWS.
forward.Update(EWS.ConflictResolutionMode.AlwaysOverwrite);
forward.Send();
UPDATE: Solution
You can read the mime content using
_serviceInstance.Users[UserId].Messages[message.Id].Content.GetAsync()
which returns a stream that can be saved as .eml file. The attachment name must end with .eml or it doesn't work.
public void ForwardMessageAsAttachment(
Message message,
IEnumerable<string> to,
string subject = null,
string body = null,
BodyType? bodyType = null)
{
// Download the mime content for the specific message
var mimeContentStream = _serviceInstance
.Users[UserId]
.Messages[message.Id]
.Content
.Request()
.GetAsync()
.GetAwaiter()
.GetResult();
// the mine content is returned as a Stream, so write it to buffer
var buffer = new Span<Byte>(new byte[mimeContentStream.Length]);
var mimeContent = mimeContentStream.Read(buffer);
// The attachment Name must ends with .eml else Outlook wont recognize its an email
// even with the contentType = message/rfc822
var messageAsAttachment = new FileAttachment
{
ContentBytes = buffer.ToArray(),
ContentType = "message/rfc822",
Name = $"{message.Subject}.eml"
};
// create a new message
var forward = new Message()
{
Attachments = new MessageAttachmentsCollectionPage(),
Subject = String.IsNullOrWhiteSpace(subject) ? subject : $"FW: {message.Subject}".Trim(),
Body = new ItemBody
{
ContentType = bodyType,
Content = body
},
ToRecipients = to.Select(x => new Recipient { EmailAddress = new EmailAddress { Address = x } })
};
// add the EML attachment
forward.Attachments.Add(messageAsAttachment);
_serviceInstance
.Users[UserId]
.SendMail(forward)
.Request()
.PostAsync()
.GetAwaiter()
.GetResult();
}
In the Graph there is no equivalent to
EWS.ItemAttachment<EWS.EmailMessage> itemAttachment =
forward.Attachments.AddItemAttachment<EWS.EmailMessage>();
itemAttachment.Item.MimeContent = msgToAttach.MimeContent;
So your options are to attach the message as an EML file as a File Attachment
or you can build the MIME message outside with a MIME parser like mailkit and then send the message as MIME https://github.com/microsoftgraph/microsoft-graph-docs/blob/main/concepts/outlook-send-mime-message.md you will be limited on size with this method you can have anything larger then 4MB and with mimebloat that usually means around 2-3 MB
Mime content from a message can be read using MS Graph and converted to .EML, then sent FileAttachment on a new message. You need to make sure the attachment's name ends with .eml or outlook will not recognize attachment as a message. Adding content type of message/rfc822 does not appear to affect how the attachment is read by gmail, hotmail or Outlook desktop.
public void ForwardMessageAsAttachment(
Message message,
IEnumerable<string> to,
string subject = null,
string body = null,
BodyType? bodyType = null)
{
// Download the mime content for the specific message
// _serviceInstance is an instance of GraphServiceClient
var mimeContentStream = _serviceInstance
.Users[UserId]
.Messages[message.Id]
.Content
.Request()
.GetAsync()
.GetAwaiter()
.GetResult();
// the mine content is returned as a Stream, so write it to buffer
var buffer = new Span<Byte>(new byte[mimeContentStream.Length]);
var mimeContent = mimeContentStream.Read(buffer);
// The attachment Name must ends with .eml else Outlook wont recognize its an email
// even with the contentType = message/rfc822
var messageAsAttachment = new FileAttachment
{
ContentBytes = buffer.ToArray(),
ContentType = "message/rfc822",
Name = $"{message.Subject}.eml"
};
// create a new message
var forward = new Message()
{
Attachments = new MessageAttachmentsCollectionPage(),
Subject = String.IsNullOrWhiteSpace(subject) ? subject : $"FW: {message.Subject}".Trim(),
Body = new ItemBody
{
ContentType = bodyType,
Content = body
},
ToRecipients = to.Select(x => new Recipient { EmailAddress = new EmailAddress { Address = x } })
};
// add the EML attachment
forward.Attachments.Add(messageAsAttachment);
_serviceInstance
.Users[UserId]
.SendMail(forward)
.Request()
.PostAsync()
.GetAwaiter()
.GetResult();
}

How can i save a REST Response to a PDF file? [duplicate]

I have a URL (URL for the live feed from client) which when I hit in browser returns the xml response . I have saved this in text file it`s size is 8 MB.
now my problem is that I need to save this response in xml file on server`s drive. from there I will insert this in database. and request needs to be made using code using http-client or rest-sharp library of c# .net 4.5
I am unsure what should I do for above case. can any body suggest me something
With RestSharp, it's right there in the readme:
var client = new RestClient("http://example.com");
client.DownloadData(request).SaveAs(path);
With HttpClient, it's a bit more involved. Have a look at this blog post.
Another option is Flurl.Http (disclaimer: I'm the author). It uses HttpClient under the hood and provides a fluent interface and lots of convenient helper methods, including:
await "http://example.com".DownloadFileAsync(folderPath, "foo.xml");
Get it on NuGet.
It seems SaveAs was discontinued. You can try this
var client = new RestClient("http://example.com")
byte[] response = client.DownloadData(request);
File.WriteAllBytes(SAVE_PATH, response);
In case you want async version
var request = new RestRequest("/resource/5", Method.GET);
var client = new RestClient("http://example.com");
var response = await client.ExecuteTaskAsync(request);
if (response.StatusCode != HttpStatusCode.OK)
throw new Exception($"Unable to download file");
response.RawBytes.SaveAs(path);
Don't keep the file in memory while reading. Write it directly to the disk.
var tempFile = Path.GetTempFileName();
using var writer = File.OpenWrite(tempFile);
var client = new RestClient(baseUrl);
var request = new RestRequest("Assets/LargeFile.7z");
request.ResponseWriter = responseStream =>
{
using (responseStream)
{
responseStream.CopyTo(writer);
}
};
var response = client.DownloadData(request);
Copied from here https://stackoverflow.com/a/59720610/179017.
Add following NuGet package into the current system
dotnet add package RestSharp
Using Bearer Authentication
// Download file from 3rd party API
[HttpGet("[action]")]
public async Task<IActionResult> Download([FromQuery] string fileUri)
{
// Using rest sharp
RestClient client = new RestClient(fileUri);
client.ClearHandlers();
client.AddHandler("*", () => { return new JsonDeserializer(); });
RestRequest request = new RestRequest(Method.GET);
request.AddParameter("Authorization", string.Format("Bearer " + accessToken),
ParameterType.HttpHeader);
IRestResponse response = await client.ExecuteTaskAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
// Read bytes
byte[] fileBytes = response.RawBytes;
var headervalue = response.Headers.FirstOrDefault(x => x.Name == "Content-Disposition")?.Value;
string contentDispositionString = Convert.ToString(headervalue);
ContentDisposition contentDisposition = new ContentDisposition(contentDispositionString);
string fileName = contentDisposition.FileName;
// you can write a own logic for download file on SFTP,Local local system location
//
// If you to return file object then you can use below code
return File(fileBytes, "application/octet-stream", fileName);
}
}
Using Basic Authentication
// Download file from 3rd party API
[HttpGet("[action]")]
public async Task<IActionResult> Download([FromQuery] string fileUri)
{
RestClient client = new RestClient(fileUri)
{
Authenticator = new HttpBasicAuthenticator("your user name", "your password")
};
client.ClearHandlers();
client.AddHandler("*", () => { return new JsonDeserializer(); });
RestRequest request = new RestRequest(Method.GET);
IRestResponse response = await client.ExecuteTaskAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
// Read bytes
byte[] fileBytes = response.RawBytes;
var headervalue = response.Headers.FirstOrDefault(x => x.Name == "Content-Disposition")?.Value;
string contentDispositionString = Convert.ToString(headervalue);
ContentDisposition contentDisposition = new ContentDisposition(contentDispositionString);
string fileName = contentDisposition.FileName;
// you can write a own logic for download file on SFTP,Local local system location
//
// If you to return file object then you can use below code
return File(fileBytes, "application/octet-stream", fileName);
}
}

How do I decode the response stream correctly in ServiceStack ProxyFeature?

I am trying to replace URLs in the body of a response in ProxyFeature, but I am getting an encoded body.
Here is the code:
Plugins.Add(new ProxyFeature(
matchingRequests: req =>
{
return req.PathInfo.StartsWith("/proxy");
},
resolveUrl: (req) =>
{
string var = req.RawUrl.Replace("/proxy/", "");
return var;
})
{
IgnoreResponseHeaders = {
"X-Frame-Options"
},
TransformResponse = async (res, responseStream) =>
{
using (var reader = new StreamReader(responseStream,Encoding.UTF8))
{
var responseBody = await reader.ReadToEndAsync();
Console.WriteLine(responseBody);
var replacedBody = responseBody.Replace("http://","/proxy/http://");
replacedBody = replacedBody.Replace("https://", "/proxy/https://");
return MemoryStreamFactory.GetStream(replacedBody.ToUtf8Bytes());
}
}
I am not sure what I am doing wrong, since this is more or less a copy of the sample code in the website with minor changes.
The outcome should be any and all URLs in the body should be prepended by
"/proxy/", and this should be applied to any URL that the user navigates to.
Good to mention that this works well enough if I remove the "TransformResponse" part.
Any ideas as to what I am doing wrong here.
Thank you.
Updates:
The partial of the content being returned. The url navigated in this case was "https://www.theverge.com".
?p?}^??d????i+s4?~?<???$?x]??????j??u?,?z?md?F6??G??{???g;?tU??q???????08;???vφ????N?? k???d8??l??GA?x???p?";?f??yk2?R?r2??
fA?z?7Q?Y}??2?v????p<|dù?s???q????t?M?^0_???o?/??V???z?S?5??r-s?N?U?j?:??1Bo?Z?>?-?
??u??{{*v????Q?g??s4?? ;?$;?Q???A0??YFo/{:;6??{?q/;?????????G????s??.??g?E?????w??'wL{?Lw0?-¬?????~????p?u??TC?X??J?j??lW??Z??(???z?u?u????a?W???~?R?t{?J?Q??f?^?6k?R?X????]^M?3??_g?????Y? *????l?xp?sT
~??9?L??4D{1q3V??r??;?'9He(??FeCTP[??/???T?{??j%??h?????#?f?e??k???p?R?&?VM????n<R?+???wR??? ????p?pyo#y??a??7L?????7VL??6n#0o,m?q????????J??#?+-Io??pr#n|????|qU?7?:??mVT?y?M??Mf ??"?=?B??u??F?X/4??f?^,?^?t????N???????fk??T!??y{?SG???a??'???#EWz?O???{???????po~?V]?Vo????Y?σ??#??2QTg??4??n????4?=???P5j!?j????}?7?M'??[??A?/[=?Q??O??? ~-^???,?/f??U?????p???A:??????M.`?.R??????8??]+???T??|o?0????????GD?_0???'{??~x?3?tM??Xe{???T0, f8!?w?j?m=??3??f?????[q?????}??a???r?????l??d[)?????p?w
The garbled output is because the downstream response that's being returned is compressed yet you're trying to read it directly as a string.
If the proxy response is compressed you'll need to decompress it, make any string transformations then compress it back, e.g:
Plugins.Add(new ProxyFeature(
matchingRequests: req => req.PathInfo.StartsWith("/proxy"),
resolveUrl: req => req.RawUrl.Replace("/proxy/", ""))
{
IgnoreResponseHeaders = { "X-Frame-Options" },
TransformResponse = async (res, responseStream) => {
var enc = res.GetHeader(HttpHeaders.ContentEncoding);
var useStream = responseStream;
if (enc != null)
useStream = responseStream.Decompress(enc);
using (var reader = new StreamReader(useStream,Encoding.UTF8))
{
var responseBody = await reader.ReadToEndAsync();
var replacedBody = responseBody.Replace("http://","/proxy/http://");
replacedBody = replacedBody.Replace("https://", "/proxy/https://");
var bytes = replacedBody.ToUtf8Bytes();
return new MemoryStream(enc != null ? bytes.CompressBytes(enc) : bytes);
}
}
});

How to read attachment content from bot framework C#?

I am writing a bot and expecting the user to send me an attachment, which I want to read and translate into objects.
I have the following code so far:
if (message.Attachments != null && message.Attachments.Any())
{
var attachment = message.Attachments.First();
using (HttpClient httpClient = new HttpClient())
{
if ((message.ChannelId.Equals("skype", StringComparison.InvariantCultureIgnoreCase) || message.ChannelId.Equals("msteams", StringComparison.InvariantCultureIgnoreCase)) && new Uri(attachment.ContentUrl).Host.EndsWith("skype.com"))
{
var token = await new MicrosoftAppCredentials().GetTokenAsync();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
var responseMessage = await httpClient.GetAsync(attachment.ContentUrl);
var contentLenghtBytes = responseMessage.Content.Headers.ContentLength; // this is populated correctly
if(attachment.Name.ToLower().Equals("opportunity.xlsx"))
{
var temp = attachment.Content; // This Content is always null, even though everything else is populated.
}
}
}
Anyone can suggest how can I read the attachment xlsx content please?
Thanks
The attachment is not available in the Content property. You first need to download the attachment using the ContentUrl and then perform whatever you want, using the response message after downloading the file.
Take a look at the Receive-Attachments C# sample.
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var message = await argument;
if (message.Attachments != null && message.Attachments.Any())
{
var attachment = message.Attachments.First();
using (HttpClient httpClient = new HttpClient())
{
// Skype & MS Teams attachment URLs are secured by a JwtToken, so we need to pass the token from our bot.
if ((message.ChannelId.Equals("skype", StringComparison.InvariantCultureIgnoreCase) || message.ChannelId.Equals("msteams", StringComparison.InvariantCultureIgnoreCase))
&& new Uri(attachment.ContentUrl).Host.EndsWith("skype.com"))
{
var token = await new MicrosoftAppCredentials().GetTokenAsync();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
var responseMessage = await httpClient.GetAsync(attachment.ContentUrl);
var contentLenghtBytes = responseMessage.Content.Headers.ContentLength;
await context.PostAsync($"Attachment of {attachment.ContentType} type and size of {contentLenghtBytes} bytes received.");
}
}
else
{
await context.PostAsync("Hi there! I'm a bot created to show you how I can receive message attachments, but no attachment was sent to me. Please, try again sending a new message including an attachment.");
}
context.Wait(this.MessageReceivedAsync);
}
You will have to use some other dll to read the excel data, if that is what you mean. Do u mean to read the contents of the excel file after it has been uploded then you could use https://github.com/ExcelDataReader/ExcelDataReader

Incorrect file recieved when user replies with attachment

My bot prompts the user for an attachment in a dialog and is suppose to receive an image from the user and save the content name, url and type of that file to variables. However, the file I am receiving is not the file the user sent. Instead its retrieving a file named 'blob' (no extension) from this link:
https://webchat.botframework.com/attachments/GkLhiqJcvH019mP0iGBKvo/0000052/0/blob?t=X5ICiUrhFas.dAA.RwBrAEwAaABpAHEASgBjAHYASAAwADEAOQBtAFAAMABpAEcAQgBLAHYAbwAtADAAMAAwADAAMAA1ADIA.mcEiHxuH0gE.VMcp6Yduqgc.4xT1ZTOvCX-B7A0nLto6eZNFrFi-0xSzGk5AKmA-EPE
That file contains:
{"type":"message","from":{"id":"9n0I1ZqSrLF","name":"You"},"locale":"en-US","timestamp":"2017-02-14T21:12:32.074Z","channelData":{"clientActivityId":"1487106689866.9060198022610071.8"}}
Here is the code for the attachment prompt. This prompt is within a dialog:
private async Task getAttach(IDialogContext context, IAwaitable<IEnumerable<Attachment>> result)
{
IEnumerable<Attachment> list = await result;
Attachment attachment = list.FirstOrDefault();
string filename = attachment.Name;
string url = attachment.ContentUrl;
string contentType = attachment.ContentType;
//Set attachmentFileNames
if (attachmentFileNames == null)
{
attachmentFileNames = "";
}
attachmentFileNames += contentType;
attachmentFileNames += ",";
numberOfFiles++;
//Set attachmentFileURLs
if (attachmentFileURLs == null)
{
attachmentFileURLs = "";
}
attachmentFileURLs += url;
attachmentFileURLs += ",";
attachmentHasBeenAdded = true;
await FirstMessageReceivedAsync(context, result);
}
Here is how I am handling attachments in the message controller. This is within the Post Task:
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
if (activity.Attachments != null && activity.Attachments.Any())
{
hasaAttachment = true;
var attachment = activity.Attachments.First();
using (HttpClient httpClient = new HttpClient())
{
// Skype & MS Teams attachment URLs are secured by a JwtToken, so we need to pass the token from our bot.
if ((activity.ChannelId.Equals("skype", StringComparison.InvariantCultureIgnoreCase) || activity.ChannelId.Equals("msteams", StringComparison.InvariantCultureIgnoreCase))
&& new Uri(attachment.ContentUrl).Host.EndsWith("skype.com"))
{
var token = await new MicrosoftAppCredentials().GetTokenAsync();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
var responseMessage = await httpClient.GetAsync(attachment.ContentUrl);
var contentLenghtBytes = responseMessage.Content.Headers.ContentLength;
Activity reply = activity.CreateReply($"Attachment of {attachment.ContentType} type and size of {contentLenghtBytes} bytes received. URL: {attachment.ContentUrl}");
await connector.Conversations.ReplyToActivityAsync(reply);
}
}
I am following this example:
Receive Attachment Bot Sample
In the emulator it works correctly but the published version on azure does not. The code is very similar to the example so I don't see why the bot is not detecting the user's files.
How do I receive the correct file (the file the user replied with)?
The attachments will be in elements 1 onwards. E.g.
Attachment attachment = list.ElementAt(1);
The webchat (and maybe other platforms) has a weird quirk where it adds a blob file at the start of IEnumerable 'result' when prompting and getting attachments from the user.
private async Task getAttach(IDialogContext context, IAwaitable<IEnumerable<Attachment>> result)
{...}

Categories