How to attach S3 file to email in Django

Question:

I’m trying to attach a media file saved in an S3 bucket to an email, which I’m doing with this line of code:

email.attach_file(standard.download.url)

The model is defined as follows:

class Standard(models.Model):
    name = models.CharField(max_length = 51)
    download = models.FileField(upload_to="standard_downloads/", null=True, blank=True)

    def __str__(self):
        return self.name

Within settings.py I have defined my media files as follows:

AWS_DEFAULT_ACL = 'public-read'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
DEFAULT_FILE_STORAGE = 'sme.storage_backends.MediaStorage'
MEDIA_ROOT = 'https://%s.s3.amazonaws.com/media/' % AWS_STORAGE_BUCKET_NAME

When trying to run the code I’m getting

No such file or directory:
‘https:/bucket-name.s3.amazonaws.com/media/standard_downloads/filename.ext

Please note it is showing as https:/ (a single /). How do I correct this?

Asked By: HenryM

||

Answers:

Here’s the source code of attach_file from Django. It clearly says – to attach file from the filesystem. It does not work with remote urls. And when you give it a url it thinks you are referring to local file, so it escapes all double slashes to single slashes.

def attach_file(self, path, mimetype=None):
    """
    Attach a file from the filesystem.

    Set the mimetype to DEFAULT_ATTACHMENT_MIME_TYPE if it isn't specified
    and cannot be guessed.

    For a text/* mimetype (guessed or specified), decode the file's content
    as UTF-8. If that fails, set the mimetype to
    DEFAULT_ATTACHMENT_MIME_TYPE and don't decode the content.
    """
    path = Path(path)
    with path.open('rb') as file:
        content = file.read()
        self.attach(path.name, content, mimetype)

Django does not provide anything built-in for that. You will have to write something custom on the lines of above code also using libraries like request or boto. Basically the idea is to fetch from remote url save as temp and then use attach on that.

Here’s one example on how you could get the file on the fly:

from django.core.mail.message import attach
import requests
response = requests.get("http://yoururl/somefile.pdf")
email.attach('My file',response.read(),mimetype="application/pdf")
Answered By: bhaskarc

A better way to do this would be to leverage default_storage which will work whether you are using local file storage, S3 or any other storage backend.

from django.core.files.storage import default_storage

msg = EmailMessage(
    subject="Your subject",
    body="Your Message",
    from_email="[email protected]",
    to=["[email protected]"],
)
    
filename = "standard_downloads/filename.ext"
with default_storage.open(filename, "r") as fh:
    msg.attach(filename, fh.read())

msg.send()
Answered By: tsantor
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.