Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending emails with attachments #7

Closed
garyng opened this issue Apr 20, 2021 · 6 comments · Fixed by #9
Closed

Sending emails with attachments #7

garyng opened this issue Apr 20, 2021 · 6 comments · Fixed by #9
Milestone

Comments

@garyng
Copy link

garyng commented Apr 20, 2021

Is your feature request related to a problem? Please describe.
I was trying to send emails that have attachments, but I wasn't able to find any methods for doing it. I briefly searched through the code and didn't find anything too.

Describe the solution you'd like
Able to add attachments to email body.

Is it possible to do this currently?
Thanks!

@hbulens
Copy link
Member

hbulens commented Apr 23, 2021

Not possible at the moment but it is a good case since every e-mail provider has its own implementation. For example, SendGrid uses SendGrid.Helpers.Mail.Attachment while SMTP uses System.Net.Mail.Attachment. You wouldn't want to work with those classes directly in your code base so I'll try to come up with a solution. Would a stream provided with some metadata be sufficient?

@garyng
Copy link
Author

garyng commented Apr 23, 2021

I think it should be? I'm am not very sure about how it will be implemented, but I think something similar to how MailKit handles attachments should be sufficient.

@hbulens
Copy link
Member

hbulens commented Apr 25, 2021

I added a simple mechanism to add attachments to the e-mail request. A snippet from an SMTP test:

SmtpCredentials credentials = new SmtpCredentials("", " ", "", "", "", "");
byte[] txtBytes = File.ReadAllBytes("Attachments\\Attachment.txt");
byte[] pdfBytes = File.ReadAllBytes("Attachments\\Attachment.pdf");

List<Attachment> attachments = new()
{
    new() { ContentBytes = txtBytes, Name = "Attachment.txt" },
    new() { ContentBytes = pdfBytes, Name = "Attachment.pdf" }
};

EmailComposer<TestMailModel> composer = new EmailComposer<TestMailModel>();
EmailRequest<TestMailModel> request = composer
    .SetModel(new TestMailModel { Email = "guy.gadbois@facteur.com", Name = "Guy Gadbois" })
    .SetSubject("Hello world")
    .SetFrom("info@facteur.com")
    .SetTo("tibipi@getnada.com")
    .SetCc("tibipi@getnada.com")
    .SetBcc("tibipi@getnada.com")
    .Attach(attachments) // This is new
    .Build();

IMailer mailer = new SmtpMailer(credentials);

IMailBodyBuilder builder = new MailBodyBuilder();
EmailRequest populatedRequest = await builder
    .UseProvider(new AppDirectoryTemplateProvider("Templates", ".sbnhtml"))
    .UseResolver(new ViewModelTemplateResolver())
    .UseCompiler(new ScribanCompiler())
    .BuildAsync(request);

await mailer.SendMailAsync(populatedRequest);

To simplify the retrieval of attachments, you can use helper classes that will ultimately end up as separate NuGet packages. For example, Facteur.Attachments.IO is a simple library that allows you to fetch files using good old System.IO. It goes without saying that this mechanism can be expanded to other storage mediums like Azure Blobs.

using Facteur.Attachments.IO;
...
IAttachmentSource fileAttachment = new Facteur.Attachments.IO.FileAttachment();
Attachment attachment = await fileAttachment.Fetch("Attachments\\Attachment.txt");

Thoughts? Enough in scope to publish a new version?

@garyng
Copy link
Author

garyng commented Apr 25, 2021

I took a quick look at the code, looks good to me!


Another relevant use case that I can think of is inlining/embedding attachment images directly into the message body (iirc by using cid for referencing the images?). I was thinking maybe we could add that... but I did some research (at least for SMTP) and seems like they are quite complicated to implement (eg: https://stackoverflow.com/a/11000938/).

I am not really familiar with other email providers, but I am guessing it would take quite some effort have a generalized interface for all of them, of course I might be wrong 😅

So the I think the current implementation is probably enough for now.

Thanks!

@hbulens
Copy link
Member

hbulens commented Apr 26, 2021

Great, I'll finalize and publish a new version soon.

About the inline attachments, can't this be covered with the templates? You could provide a placeholder in the template which is then populated by a URL property in the mail model? Images could be converted into data uris as to not rely on external content. Haven't tested that yet but it could be an easier way than cid, which looks quite complicated indeed. Fair point indeed and I'll create an issue for it.

@garyng
Copy link
Author

garyng commented Apr 26, 2021

Yeah I agree. In most of the cases using image url is much simpler, but iirc base64-encoded image doesn't work reliably for most email clients (eg. gmail) - a lot of the solutions that I've seen seem to suggest using cid instead.


Anyway thanks again!

hbulens added a commit that referenced this issue Apr 27, 2021
@hbulens hbulens linked a pull request Apr 27, 2021 that will close this issue
hbulens added a commit that referenced this issue Apr 27, 2021
hbulens added a commit that referenced this issue Apr 27, 2021
hbulens added a commit that referenced this issue May 11, 2021
@hbulens hbulens added this to the 1.1.0 milestone Oct 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants