Help formatting an image as an email attachment using Nodemailer

Hello nice Node-RED people, I come to you for help.

I want to send an email to notify me when Frigate detects a person. Frigate is configured to use MQTT, and the image data arrives in an MQTT message.

At first I tried to use the data that is sent in msg.payload directly, as described in the email node Help.

I got nowhere, except for sometimes I managed so send the image as body text, thus not an image, just a long jumble of garbage.

Then I thought it was an email server problem, but when I talked to my ISP, they said their server is rejecting my emails because of a dangerous attachment. That must mean I'm formatting the data wrong, and an email with no attachment goes through just fine.

So then I thought, maybe the data being sent by Frigate is rubbish, so now I'm I responding to the frigate message by pulling a frame directly from the camera, and then trying to format it in Function node.

I can see that the image is pulled correctly, because the image shows up in a parallel viewer node, but the image doesn't survive my Function node.

I am trying to use Nodemailer format to send the image, but I have not been able to find a specific example anywhere where the nodemailer format is sending an IMAGE

Please. I know that this should be an easy thing, and I have read a zillion threads, but I can't get this right and I'm desperate.

What am I doing wrong? Please put me out of my misery.

Here is my stupid flow.

[
    {
        "id": "6a4432f18e678736",
        "type": "inject",
        "z": "2d5ffc4e4395cb95",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "true",
        "payloadType": "bool",
        "x": 670,
        "y": 360,
        "wires": [
            [
                "d8e0cb4213559ed2"
            ]
        ]
    },
    {
        "id": "e56da89e8c5de884",
        "type": "e-mail",
        "z": "2d5ffc4e4395cb95",
        "server": "redacted",
        "port": "465",
        "authtype": "BASIC",
        "saslformat": true,
        "token": "oauth2Response.access_token",
        "secure": true,
        "tls": true,
        "name": "fake.name@fake.com",
        "dname": "Email out",
        "x": 1380,
        "y": 320,
        "wires": []
    },
    {
        "id": "2a1754109ab7afb2",
        "type": "image viewer",
        "z": "2d5ffc4e4395cb95",
        "name": "",
        "width": "704",
        "data": "payload",
        "dataType": "msg",
        "active": true,
        "x": 1050,
        "y": 420,
        "wires": [
            []
        ]
    },
    {
        "id": "d8e0cb4213559ed2",
        "type": "hikvisionUltimatePicture",
        "z": "2d5ffc4e4395cb95",
        "name": "",
        "topic": "",
        "server": "87739017a94d3b9b",
        "channelID": "6",
        "rotateimage": "0",
        "heightimage": "",
        "widthimage": "",
        "qualityimage": "100",
        "cropimage": "",
        "textoverlay": "",
        "textoverlayXY": "0,0",
        "textoverlayWH": "0,0",
        "textoverlayFont": "FONT_SANS_32_WHITE",
        "urlImageCurrentIndex": 0,
        "x": 880,
        "y": 360,
        "wires": [
            [
                "df8a62381ef91052",
                "2a1754109ab7afb2"
            ],
            []
        ]
    },
    {
        "id": "df8a62381ef91052",
        "type": "function",
        "z": "2d5ffc4e4395cb95",
        "name": "Prepare Email",
        "func": "var image = Buffer.from([msg.payload]);\nflow.set(\"image1\", msg.payload);\nmsg.attachments = [{\n    filename: 'image.jpg',\n     content: flow.get(\"image1\"),\n     contentType: 'image/jpeg',\n     encoding: 'base64'\n}];\nmsg.topic = \"testing testing\"\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1080,
        "y": 360,
        "wires": [
            [
                "e56da89e8c5de884"
            ]
        ]
    },
    {
        "id": "87739017a94d3b9b",
        "type": "Hikvision-config",
        "host": "192.168.3.33",
        "port": "80",
        "name": "House NVR",
        "authentication": "digest",
        "protocol": "http",
        "heartbeattimerdisconnectionlimit": "2",
        "deviceinfo": "{\"?xml\":\"\",\"DeviceInfo\":{\"deviceName\":\"House NVR\",\"deviceID\":\"48363630-3737-3734-3636-a41437956562\",\"model\":\"DS-7616NI-E2\",\"serialNumber\":\"DS-7616NI-E21620161013AARR660777466WCVU\",\"macAddress\":\"a4:14:37:95:65:62\",\"firmwareVersion\":\"V3.4.96\",\"firmwareReleasedDate\":\"build 171128\",\"encoderVersion\":\"V5.0\",\"encoderReleasedDate\":\"build 170830\",\"deviceType\":\"IPC\",\"telecontrolID\":255}}",
        "debuglevel": "no"
    }
]

What does the hikvisionUltimatePicture node output - add a debug (set to show complete msg) and show us what it outputs.

Regarding your function, the first line var image = Buffer.from([msg.payload]); is definitely wrong (but also probably not needed since hikvisionUltimatePicture likely outputs a buffer). PS, also no need to store in context.

1 Like

Hi @Steve-Mcl

Here is the message that comes out of the Hikvision node:

{"topic":"","payload":"data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAkACwAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APu/xz/p+uKNP/4lX9sFv7B/sP0x/nn+tftB+A4v4X8vziav2DUvCfifVPBOn8apo+vf8JD/AMSP/mZD/n8j60HyuJ/iP+vsxOXsb/Ur/wD4pvxBj/hF/wC3h/b3OP8AP69+tewewd7on9paFrX/ABR39keANL/t7wb/AMVwKA...","previousInputMessage":{"_msgid":"0d672616795d7a31","payload":true,"topic":""},"forEmail":[255,216,255,224,0,16,74,70,73,70,0,1,1,0,0,1,0,1,0,0,255,219,0,132,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,255,192,0,17,8,2,64,2,192,3,1,17,0,2,17,1,3,17,1,255,196,1,162,0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,16,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,125,1,2,3,0,4,17,5,18,33,49,65,6,19,81,97,7,34,113,20,50,129,145,161,8,35,66,177,193,21,82,209,240,36,51,98,114,130,9,10,22,23,24,25,26,37,38,39,40,41,42,52,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,225,226,227,228,229,230,231,232,233,234,241,242,243,244,245,246,247,248,249,250,1,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,17,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,119,0,1,2,3,17,4,5,33,49,6,18,65,81,7,97,113,19,34,50,129,8,20,66,145,161,177,193,9,35,51,82,240,21,98,114,209,10,22,36,52,225,37,241,23,24,25,26,38,39,40,41,42,53,54,55,56,57,58,67,68,69,70,71,72,73,74,83,84,85,86,87,88,89,90,99,100,101,102,103,104,105,106,115,116,117,118,119,120,121,122,130,131,132,133,134,135,136,137,138,146,147,148,149,150,151,152,153,154,162,163,164,165,166,167,168,169,170,178,179,180,181,182,183,184,185,186,194,195,196,197,198,199,200,201,202,210,211,212,213,214,215,216,217,218,226,227,228,229,230,231,232,233,234,242,243,244,245,246,247,248,249,250,255,218,0,12,3,1,0,2,17,3,17,0,63,0,251,191,199,63,233,250,226,141,63,254,37,95,219,5,191,176,127,176,253,49,254,121,254,181,251,65,248,14,47,225,127,47,206,38,175,216,53,47,9,248,159,84,240,78,159,198,169,163,235,223,240,144,255,0,196,143,254,102,67,254,127,35,235,65,242,184,159,226,63,235,236,196,229,236,111,245,43,255,0,248,166,252,65,143,248,69,255,0,183,135,246,247,56,255,0,63,175,126,181,236,30,193,222,232,159,218,90,22,181,255,0,20,119,246,71,128,52,191,237,239,6,255,0,197,112,40,3,210,127,225,90,124,35,215,188,47,227,191,248,82,30,55,213,245,95,20,124,56,255,0,139,133,253,185,174,104,31,241,73,120,144,124,54,241,255,0,252,211,175,161,227,140,246,53,151,215,23,187,122,82,92,206,214,230,77,195,220,114,188,236,172,172,215,39,184,230,185,156,108,236,219,94,129,230,30,7,248,73,168,124,75,241,62,149,225,191,7,233,250,71,246,160,215,191,225,30,215,184,63,248,112,63,150,63,165,83,197,193,38,218,209,38,222,175,101,171,251,32,116,255,0,21,62,3,120,103,65,209,124,47,227,111,135,254,39,213,252,85,224,62,124,61,175,107,154,232,63,242,56,251,127,245,254,159,87,245,168,255,0,47,226,255,0,249,16,56,79,10,248,71,195,58,245,239,252,92,13,83,72,240,175,133,252,31,255,0,21,7,252,72,193,255,0,132,187,196,158,50,247,255,0,60,81,245,168,255,0,47,226,255,0,249,16,25,241,195,225,55,134,188,9,240,251,192,159,22,124,31,169,248,187,85,240,191,196,113,227,47,249,30,52,47,248,171,128,248,109,211,63,145,228,245,228,118,53,227,169,197,202,80,82,78,81],"base64":"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAkACwAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APu/xz/p+uKNP/4lX9sFv7B/sP0x/nn+tftB+A4v4X8vziav2DUvCfifVPBOn8apo+vf8JD/AMSP/mZD/n8j60HyuJ/iP+vsxOXsb/Ur/wD4pvxBj/hF/wC3h/b3OP8AP69+tewewd7on9paFrX/ABR39keANL/t7wb/AMVwKAPSf+FafCPXvC/jv/hSHjfV...","_msgid":"a52aba0ce71e7fe5"}

Per your comments, this is my Function node.

This does send an email, but the attachment is unreadable.

The function text is:

msg.attachments = [{
    filename: 'image.jpg',
     content: msg.payload,
     contentType: 'image/jpeg',
     encoding: 'base64'
}];
msg.topic = "testing testing"
msg.payload = "I hope this works"
return msg;
[
    {
        "id": "6dcb139a68017208",
        "type": "function",
        "z": "21e11e4ca2b93801",
        "name": "Prepare Email",
        "func": "msg.attachments = [{\n    filename: 'image.jpg',\n     content: msg.payload,\n     contentType: 'image/jpeg',\n     encoding: 'base64'\n}];\nmsg.topic = \"testing testing\"\nmsg.payload = \"I hope this works\"\nreturn msg;",
        "outputs": 1,
        "timeout": "",
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 1520,
        "y": 720,
        "wires": [
            [
                "85436bb879bd7f52"
            ]
        ]
    }
]

ok, so it returns a base64 image string.

the image-tools "image" node can do the base64 to buffer conversion.

[{"id":"9e4c431727221940","type":"inject","z":"20165f674cef3579","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":1160,"y":700,"wires":[["61297201e8f2d924"]]},{"id":"61297201e8f2d924","type":"function","z":"20165f674cef3579","name":"simulate hik camera","func":"msg = {\n    \"topic\": \"\",\n    \"payload\": \"data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC\"\n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1360,"y":700,"wires":[["62576c1c034bcb4c"]]},{"id":"62576c1c034bcb4c","type":"jimp-image","z":"20165f674cef3579","name":"base64 img to buffer","data":"payload","dataType":"msg","ret":"buf","parameter1":"","parameter1Type":"msg","parameter2":"","parameter2Type":"msg","parameter3":"","parameter3Type":"msg","parameter4":"","parameter4Type":"msg","parameter5":"","parameter5Type":"msg","parameter6":"","parameter6Type":"msg","parameter7":"","parameter7Type":"msg","parameter8":"","parameter8Type":"msg","sendProperty":"buffer","sendPropertyType":"msg","parameterCount":0,"jimpFunction":"none","selectedJimpFunction":{"name":"none","fn":"none","description":"Just loads the image.","parameters":[]},"x":1590,"y":700,"wires":[["04ba9b37ba13b900"]]},{"id":"04ba9b37ba13b900","type":"image viewer","z":"20165f674cef3579","name":"","width":160,"data":"buffer","dataType":"msg","active":true,"x":1310,"y":760,"wires":[["69957e686cdfa0f4"]]},{"id":"69957e686cdfa0f4","type":"change","z":"20165f674cef3579","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"A new image...","tot":"str"},{"t":"set","p":"payload","pt":"msg","to":"See attached","tot":"str"},{"t":"set","p":"attachments","pt":"msg","to":"[{}]","tot":"json"},{"t":"set","p":"attachments[0].filename","pt":"msg","to":"image.jpg","tot":"str"},{"t":"set","p":"attachments[0].content","pt":"msg","to":"buffer","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1500,"y":760,"wires":[["cd6e6a759b81a782"]]},{"id":"cd6e6a759b81a782","type":"debug","z":"20165f674cef3579","name":"to email node","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1720,"y":760,"wires":[]}]
1 Like

@Steve-Mcl I could kiss you.

I work three fulltime jobs; this has held me back for months.

Here is what I learned from you.

  1. A base64 image wants to be a buffer if it's to be an email attachment (V IMPORTANT).
  2. You can set the properties of an array or an object that easily with a change node.

That was the eyeopener for me. I get how objects and arrays work, but it had never occurred to me to use a change node to define an array before.

I got that because you defined an array as [{}], and then proceeded to fill it with things. It's totally obvious in retrospect, but it's the sort of thing I've always used a function node for.

Generally I still would use a function node, because there would be other jiggery pokery going on too, but for this kind of flow, this is the sweetest solution I've ever seen, and I learned new things from it.

Thank you; you're very kind. Have a lovely day.

2 Likes

If you already have an image URI, why not just that in your email directly? You should be able to insert it direct into an img tag in an HTML email body. No attachments required.

1 Like