0

I am using Rotativa to convert my view to pdf. I would like to send that generated pdf as an email attachment (without having to download it first to disk). I've been following a bunch of tutorials to do this but I just keep going round in circles. I would much appreciate any help I can get.

public async Task<IActionResult>SomeReport()
{
...
return new ViewAsPdf (report)
}
return view();


MemoryStream memoryStream = new MemoryStream();

MimeMessage msg = new MimeMessage();
MailboxAddress from = new MailboxAddress ("Name", "emailAddress")
msg.From.Add(from);
MailboxAddress to = new MailboxAddress ("Name", "emailAddress")
msg.From.Add(to);
BodyBuilder bd = new BodyBuilder();
bb.HtmlBody ="some text";
bb.Attachments.Add("attachmentName", new MemoryStream());
msg.Body = bb.ToMessageBody();
SmtpClient smtp = new SmtpClient();
smtp.Connect("smtp.gmail.com",465, true);
smtp.Authenticate("emailAddress", "Pwd");
smtp.Send(msg);
smtp.Disconnect(true);
smtp.Dispose();

Edit

Parent View from which Email is sent

@Model MyProject.Models.EntityViewModel 
<a asp-action= "SendPdfMail" asp-controller ="Student" asp-route-id = "@Model.Student.StudentId">Email</a>
 ...


SendPdfMail action in Student Controller
public async Task<IActionResult> SendPdfMail(string id)
{  
  var student = await context.Student. Where(s => s.StudentId == id);
  if (student != null)
  {
   ...
   var viewAsPdf = new ViewAsPdf("MyPdfView", new{route = id})
  {      
           Model = new EntityViewModel(),
            FileName = PdfFileName,

        ...
    }
   }
    };
Omo
  • 23
  • 1
  • 8
  • how are you creating your pdf as a memory stream, in you example the attachment add with `new MemoryStream()` will create a new empty emory stream. – Aron Lawrence Aug 16 '20 at 01:52

2 Answers2

3

Complete answer using Rotativa.AspNetCore. Code is developed in VS 2019, Core 3.1, Rotativa.AspNetCore 1.1.1.

Nuget

Install-package Rotativa.AspNetCore

Sample controller

public class SendPdfController : ControllerBase
{
    private const string PdfFileName = "test.pdf";

    private readonly SmtpClient _smtpClient;

    public SendPdfController(SmtpClient smtpClient)
    {
        _smtpClient = smtpClient;
    }

    [HttpGet("SendPdfMail")] // https://localhost:5001/SendPdfMail
    public async Task<IActionResult> SendPdfMail()
    {
        using var mailMessage = new MailMessage();
        mailMessage.To.Add(new MailAddress("a@b.c"));
        mailMessage.From = new MailAddress("c@d.e");
        mailMessage.Subject = "mail subject here";

        var viewAsPdf = new ViewAsPdf("view name", <YOUR MODEL HERE>)
        {
            FileName = PdfFileName,
            PageSize = Size.A4,
            PageMargins = { Left = 1, Right = 1 }
        };
        var pdfBytes = await viewAsPdf.BuildFile(ControllerContext);

        using var attachment = new Attachment(new MemoryStream(pdfBytes), PdfFileName);
        mailMessage.Attachments.Add(attachment);

        _smtpClient.Send(mailMessage); // _smtpClient will be disposed by container

        return new OkResult();
    }
}

Options class

public class SmtpOptions
{
    public string Host { get; set; }
    public int Port { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
}

In Startup#ConfigureServices

services.Configure<SmtpOptions>(Configuration.GetSection("Smtp"));

// SmtpClient is not thread-safe, hence transient
services.AddTransient(provider =>
{
    var smtpOptions = provider.GetService<IOptions<SmtpOptions>>().Value;
    return new SmtpClient(smtpOptions.Host, smtpOptions.Port)
    {
      // Credentials and EnableSsl here when required
    };
});

appsettings.json

{
  "Smtp": {
    "Host": "SMTP HOST HERE",
    "Port": PORT NUMBER HERE,   
    "Username": "USERNAME HERE",
    "Password": "PASSWORD HERE"
  }
}
Roar S.
  • 8,103
  • 1
  • 15
  • 37
  • I am getting an error with the PdfHelper class. I am using Rotativa.AspNetCore version 1.1.1. and it does not seem to have PdfHelper – Omo Aug 16 '20 at 04:04
  • @Tee: I've updated my answer with Rotativa.AspNetCore – Roar S. Aug 16 '20 at 04:36
  • Thank you so much. Everything works fine but when I run the app, I'm getting this error "InvalidOperationException: The model item passed into the ViewDataDictionary is of type '<>f_AnonymousType0'2[System.Int32,System.String]], but this ViewDataDictionary instance requires a model item of type 'MyProject.Models.EntityViewModel'' at the line "var pdfBytes = await viewAsPdf.BuildFile(ControllerContext);" – Omo Aug 16 '20 at 08:15
  • @Tee: You are failing to pass in your model class. use var viewAsPdf = new ViewAsPdf("view name", ) – Roar S. Aug 16 '20 at 17:58
  • @Tee: Forgot to mention that I updated my answer with required code changes on your side. – Roar S. Aug 17 '20 at 02:32
  • I really can't thank you enough.Thanks you so very much!!! If I could trouble you one last time :( please, the pdf attachment is not getting delivered with the content of the view that I specified. The pdf should contain the record for any desired entity. In my parent view, I am able to get this by specifying an asp-route-id in the that I use to send the email but this has no effect on the content of the attachment that gets sent. I have tried to pass an id param in the SendPdfMail action but it's not working. Can you please point me in the right direction? Thanks again for your help. – Omo Aug 17 '20 at 07:48
1

There's not quite enough to go on, but you need something like this:

MimeMessage msg = new MimeMessage();
MailboxAddress from = new MailboxAddress ("Name", "emailAddress");
msg.From.Add(from);
MailboxAddress to = new MailboxAddress ("Name", "emailAddress");
msg.To.Add(to);
BodyBuilder bb = new BodyBuilder();
bb.HtmlBody ="some text";
using (var wc = new WebClient())
{
    bb.Attachments.Add("attachmentName",wc.DownloadData("Url for your view goes here"));
}
msg.Body = bb.ToMessageBody();
using (var smtp = new SmtpClient())
{
    smtp.Connect("smtp.gmail.com",465, true);
    smtp.Authenticate("emailAddress", "Pwd");
    smtp.Send(msg);
    smtp.Disconnect(true);
}

Notice this adds the attachment before calling .ToMessageBody(), as well as fixing at least four basic typos.

But I doubt this will be enough... I suspect ViewAsPdf() needs more context than you get from a single DownloadData() request, and you'll need to go back to the drawing board to be able to provide that context, but this at least will help push you in the right direction.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794