Search code examples
fluttergoogle-cloud-firestoresendgridfirebase-extensions

Flutter with Firebase using Sendgrid extension- Attachments are broken


I'm trying to send an email with an image attachment, using Firestore with the Trigger Email Sendgrid extension found here.

Flutter code:

await db.collection('mail').add({
  'from': 'xxx@yyy.com',
  'to': email.to,
  'message': {
    'subject': email.subject,
    'html': '<img alt="QR code" src="cid:qr"/>',
  },
  'attachments': [
    {
      'filename': "qr.jpeg",
      'content': email.image, //Base 64 string
      'content_id': "qr",
      "type": "image/jpeg",
      "disposition": "inline",
    }
  ]
});

image is:

data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAYAAACAvzbMAAAAAXNSR0IArs4c6...more encoding

On the database side everything seems fine:

Firestore

Checking Gmail I'm getting a broken image:

Broken

I have checked on Mailinator as well, same result. I've seen very little documentation on this and would appreciate any help on the matter.

Update : I dropped the metadata.

Base64:

email.image = "iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAYAAACAvzbMAAAAAXNSR0IArs4c6QAAAARzQklUCAgICHwIZIgAAAyZSURBVHic7d3tbSPJFYbRa8MBOITOYELYDoEZ2JkMJxM5k1IGdgYdgjJY/1APdgda25i3iq5i6xyA0K++uGx+PAsuMFUFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD/elBc/fzwfW083EFe/W/T1v1348r7cGaWj3gc/uX0QNPe1V9fdBs5muzFxhkrzHv09Z5/V7X2oM1tdED/zx6IACfg4AAEBEQACICAkBEQACICAgAEQEBICIgAEQEBICIgAAQERAAIgICQERAAIgICAARAQEg8qjzQEZ4reucO7GKX8qBQb/XFpmxLTJjBJ/b8Zb93K4ckFZV98k7XM3XWvSNOEkrX3ajtfK5HW3Zz62fsGC+Y5EZ8FMEBICIgAAQERAAIgICQERAAIgICAARAQEgIiAARAQEgIiAABAREAAiAgJAREAAiAgIAJGVzwMZYa9F/x39QKvrnF2xV//r0qr/fqyyR+/1o2asYi+f26fwGQLydfYSA7XZCwyy15jXpXVev9cae7QBM65kL5/bp+AnLAAiAgJAREAAiAgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBICIgAEQEBIDI1c8DYU3NjB/stcbBVvBTBIQZWq3xZddqjT32WuNgK/gpfsICICIgAEQEBICIgAAQERAAIgICQERAAIgICAARAQEgIiAARAQEgIiAABAREAAiAgJAREAAiDgPhGe11xqHMI3Y49Z5fVXVNmAG/BQB4VnttcYhTHuN2QOejp+w4BqO2Qvw+QgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBICIgAEQEBICIgAAQERAAIlc/D6TNXmCgNnuBxWwDZtzOOUfnjBFeq+817rl2NW32AgO12Qs80mcISJu8A+v6cj5me62qe3mvftfKvXgKfsLiWR2zFxiolS9MnpCAABAREAAiAgJAREAAiAgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBILLyeSC/VNXX2UtczD57gdNe/bu8VdW3zhm3GnMeSO9hUG/1fh5Ij9a5wyg+t+Ptsxf4T1YOyF4L3zi67NX/JfOt+r90t+oPyIjDoO415ku3Z4dR9vK5/TT8hMVndgyY0WqNL274vxMQACICAkBEQACICAgAEQEBICIgAEQEBICIgAAQERAAIgICQERAAIgICAARAQEgIiAARB51Hkh70FzmawNmbGb8oF1oBmtqjxj6yIC0B82Gq2m1xuel1Rp78CT8hMUMhxnw/AQEgIiAABAREAAiAgJAREAAiAgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAkUedB7Kfjx7b+feYPKNV/xkJe13nfoyYceu49rvtQjP2us77o9V1zhTZq/91aXWd+/HBIwPy9UGzZ2id1+91rfvBWHtd6/3RZi8wyF5jXpc2YMaS/ITFszouNAOekoAAEBEQACICAkBEQACICAgAEQEBICIgAEQEBICIgAAQERAAIgICQERAAIgICAARAQEg8qjzQLZBc16r79/Sv1XVl84dts7rR80YZYV7OmKPt6q6d+7wVlXfBsxYYY9VXpeea7/ba40Dtka8Lq3z+qpPeLDVS1X92vlo1X/TRuzx0rnDqD1GPFqtcU9H7HEfsMe9c4eV9ngZsEer/tdlhHvN/6yMel1GuNeiz+VRP2EdA2a06i/m0b3FOjNGaLXGPW31RP819CSOATNaeV34Cf4fCAARAQEgIiAARAQEgIiAABAREAAiAgJAREAAiAgIABEBASAiIABEBASAiIAAEBEQACKPOlCqDZixykE9rfP6UTO28+/RMWOVezpij1vn9VXrHBa2yozbOefomNGq//2+dV4/ypXux4gZT+Veix6i8sTutcY9HbHHiMfLgOfycqE9RjzuF3ouV7ofLwP2+MBPWHxmhxlLOmYvsJhjkRkfCAgAEQEBICIgAEQEBICIgAAQERAAIgICQERAAIgICAARAQEgIiAARAQEgIiAABAREAAif5q9wH+xn48eb1X1184ZrfoPhNmr/7ls59+jY8aI+7HKjFtVfemc8Y+q+nvnjJeq+lvnjH9V1T+r77UdcT+qql6r7/2+yms7ylXux4j3+qdzrzUOhBmxxyqH24x4LiP2eBmwx8sie6zyaNX/Hzr3BZ6H+/Hx8dL5PP6Qn7B4VseFZqyi1Zjjl6+i1XXux/GIoQICQERAAIgICAARAQEgIiAARAQEgIiAABAREAAiAgJAREAAiAgIABEBASAiIABEBASAyF9mL/BgbcCMt+o/v+I2YI8RbvV+MNXRMeOtqr517tE6rx81Y6XXtvfgohGHDm2d11eNeV1WOVBqGzCjDZixymtL4F7zD4NZ6XHvuZmLudf8+/lrjTm46GXAHi+dO4zyUvNfk6vdj5dHLOYnLJivVf9/qR7dW6xzONYxe4HTMXuB07HIjA8EBICIgAAQERAAIgICQERAAIgICAARAQEgIiAARAQEgIiAABAREAAiAgJAREAAiAgIAJGrHyg1QhswYzv/HheY0Tqu/W6v/vMvtvPv0TFjxOFYI/ZonTtUjTkwaMSMEbbZC5y22QuctkVmfCAg/1urMR9wfrNX1dfZS9R7PO6zl4Bn5ScsuIZjkRkjHLMXOB2zFzgdi8z4QEAAiAgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBICIgAEQEBICIgAAQedR5IHv1HxjEmlr1n4+ydW8xxjZ7gdNe/Z+XW/8ay9yPbfYCp232AqdtkRkfPDIgKxwYxGO02QtczF4+LzwhP2ExwzF7gdMxe4HFHLMXOB2zFzgdsxc4HYvM+EBAAIgICAARAQEgIiAARAQEgIiAABAREAAiAgJAREAAiAgIABEBASAiIABEBASAiIAAEHnUeSAjvJZzJ0b7pdY46GubvcDpVu+7HB0zWvW/T3uvr3p/Ll86Z2wD9hhhm73AaRswY68LHxa2ckBaVd0n73A1X2uNgKziS/V/6VaNCUjvjK3GPBfG2uvCh4X5CYsZjtkLXNCxyIwRjtkLnI7ZCwx0PGKogAAQERAAIgICQERAAIgICAARAQEgIiAARAQEgIiAABAREAAiAgJAREAAiAgIABEBASCy8nkgI+x1nfMvWl3ngK1t9gK/03twWc+13+114UOHAtvsBU7bgBltwIzt/Ht0zGjdW/yBzxCQKx3m0mYvcDGv9X5oWZu7xuXep/ym1fz318P4CYsZjtkLnFpd+MMdOGYvcDpmL3A6Zi+wOgEBICIgAEQEBICIgAAQERAAIgICQERAAIgICAARAQEgIiAARAQEgIiAABAREAAiAgJA5OrngbCmNmDGraq+dM7YBuyxV/9hUG9V9a1zxir3Y4Rt9gKnbcCMvfrfH60WPXZAQJihVf8HYqv+L8wR9uo/DOpbvR9s1WOrNe4HP9przGFhbcCM4fyExbM6FpmximORGSMcsxc4HbMXWJ2AABAREAAiAgJAREAAiAgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBIOI8EGbYa41DmN6q/xyOW+f1VWMOLmqLzBhhGzTntfqeU8+1I2eMeJ+2esDrKyDMsNcahzDdB+yxilbrBGAFr/X++ra5awx5Xe616KFUfsKC+Y7ZCyzmGDCj1fx4XJ6AABAREAAiAgJAREAAiAgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBIHL180Da7AUGarMXGGgbMON2zjk6Zow4lOpWVV8GzNiq77ls59+eGa3WeJ+1ATNWOYRprzUOT2ud1/+hzxCQNnkHHuNL9X9xjziUahuwx4jnMkqbvUBd6xCmvdY4PO0h/ITFDMfsBQY6Zi8AswgIABEBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBICIgAEQEBICIgAAQERAAIiufB/JLjfn3/PnNPnuB0zZozmv1ndfQc+1324AZVf3PZTv/Hh0zrnQI063z+qoxr+2IGSMOHGv1gLNeVg7IXut84bGe13r/smtz1xhiledyr+scwnQlow4cawNm/MBPWMxwDJjRav4XbtW1ngs/OhaZsSwBASAiIABEBASAiIAAEBEQACICAkBEQACICAgAEQEBICIgAEQEBICIgAAQERAAIgICQORR54G0B81lvnahGSO0RWaM0C40Yzv/Hh0zWvcW13ouAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADweP8GqXuVqGFRNZkAAAAASUVORK5CYII="

Update 2 Generating base64:

I'm using the Screenshot flutter package to capture the qr image.

 void didChangeDependencies() {
   super.didChangeDependencies();
  _captureAndSaveQRCode();
 }

Future<void> _captureAndSaveQRCode() async {
final image = await _screenshotController.capture();
var base64 = base64Encode(image!);
if (_emailSender != null) {
  await _emailSender!.sendEmail(
    Email(
        to: widget.data.userData.email,
        subject: 'Qr Code',
        body:'Hello',
        image: base64),
  );
}

}

And the widget:

   Screenshot(
            controller: _screenshotController,
            key: _renderObjectKey,
            child: QrImage(
              data: widget.data.userData.phoneNumber,
              size: 200,
            ),
          ),

Solution

  • When you send an attachment in an email with SendGrid, the content of the attachment should just be the base 64 encoded string of the contents of the file. It should not be a data URL.

    So just use:

    email.image = "iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAYAAACAvzbMAAAAAXNSR0IArs4c6...more encoding"
    

    without the data:image/jpeg;base64, part.

    Edit

    Looked into this further and, first I need to just clear one thing up. The Firebase Trigger Email extension is a Firebase written, SMTP based extension. It is not specific to SendGrid, but can be used with SendGrid to send emails. It can also be used with other providers. The email format that you need to use in the Firebase document is generic and does not match the SendGrid email documentation.

    That being said, it was not straightforward to track down how an attachment should be added to the document in order to send it successfully with SendGrid. I did find it though, and here is the document that I added to my collection that successfully sent the QR code as an email through SendGrid:

    await db.collection('mail').add({
      'from': 'xxx@yyy.com',
      'to': email.to,
      'message': {
        'subject': email.subject,
        'html': '<img alt="QR code" src="cid:qr"/>',
        'attachments': [
          {
            'filename': "qr.jpeg",
            'content': email.image, //Base 64 string
            'cid': "qr",
            'contentType': "image/jpeg",
            'contentDisposition': "inline",
            'encoding': 'base64'
          }
        ]
      }
    });
    

    As you can see, the attachments array goes inside the message map. The properties of an attachment are different too, I could only find this interface of an attachment from the source code to get the right property names.

    I also added an encoding property, set to 'base64'.

    I was able to successfully send the QR code using your base64 encoded string with this format.