I’m trying to include remote images as email attachments but running into issues with my current setup.
My tech stack includes:
- Mailgun for email delivery
- Cloudinary for image hosting
- MongoDB for data storage
- Node.js with Express for the backend
Here’s my current workflow:
- Users upload photos to my platform
- Images get stored on Cloudinary and URLs are saved to MongoDB
- Email notifications are sent with image links in the message body
The problem is users have to click each link separately to view images. I want to attach the actual images to make downloading easier.
From what I can tell in the Mailgun docs, it seems like only local files can be attached. Am I missing something here?
When I try using attachment or inline options, I get file not found errors.
var imageList = [];
data.photos.forEach(function(img){
imageList.push(img.url + " ");
return imageList;
});
var fileAttachment = new mailgun.Attachment({data: imageList[0], filename: "photo"});
var emailData = {
from: "noreply <[email protected]>",
to: "[email protected]",
subject: 'New post notification',
html: 'Check out this new post with attached images',
attachment: fileAttachment
};
I keep getting this error:
Error: ENOENT: no such file or directory, stat 'https://res.cloudinary.com/myaccount/image/upload/photo.jpg '
I want emails with actual image attachments instead of just links. How can I make this work with remote image URLs?
yeah you cant just pass urls to mailgun like that, it expects actual file data. try downloading the images first with axios or fetch, then pass the buffer to mailgun. something like const response = await axios.get(imageUrl, {responseType: 'arraybuffer'});
then use response.data
for the attachment data instead of the url.
I ran into this exact problem about six months ago. The core issue is that you’re passing a URL string where Mailgun expects binary data. You need to fetch the remote images first before attaching them.
One thing the other responses didn’t mention is handling the Content-Type properly. When I implemented this, I had to extract the image format from the Cloudinary URL and set the correct filename extension. Also consider using streams instead of loading everything into memory if you’re dealing with large images or multiple attachments.
For production use, I’d recommend adding error handling for failed downloads and maybe implementing a retry mechanism since remote fetches can fail. You might also want to add a timeout to prevent hanging requests from blocking your email queue.
The issue is that Mailgun’s attachment feature requires actual file data, not URLs. You need to fetch the remote images first before creating attachments. Here’s what worked for me in a similar situation:
const https = require('https');
const http = require('http');
function downloadImage(url) {
return new Promise((resolve, reject) => {
const client = url.startsWith('https') ? https : http;
client.get(url, (response) => {
const chunks = [];
response.on('data', chunk => chunks.push(chunk));
response.on('end', () => resolve(Buffer.concat(chunks)));
}).on('error', reject);
});
}
// Then use it like this
const imageBuffer = await downloadImage(imageList[0]);
const fileAttachment = new mailgun.Attachment({
data: imageBuffer,
filename: "photo.jpg"
});
This approach downloads the image data into memory first, then passes the buffer to Mailgun. Keep in mind this increases memory usage if you’re processing multiple large images simultaneously.
just hit this issue last week, you gotta download the images first before mailgun can attach them. i used node-fetch to grab the image data then passed the buffer to the attachment. dont forget to set proper filename extensions or some email clients get confused about the image type.
You’re essentially trying to use URLs as file paths which won’t work. I’ve dealt with this before and found that using the built-in fetch API works well for downloading remote images. The key thing is to handle the response properly and convert it to a buffer before passing to Mailgun. Something like const response = await fetch(imageUrl); const buffer = await response.buffer();
then use that buffer in your attachment. Make sure to also handle cases where the remote image might be unavailable or the download fails, otherwise your entire email sending process could break. I usually wrap the image fetching in a try-catch block and send the email without attachments if the download fails rather than failing completely.