Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django email attachements sends as "noname"

from django.template import loader, Context
from django.core.mail import EmailMultiAlternatives, EmailMessage
from settings import ADBOARD_SETTINGS
import settings


def send_mail(mail_to, subj, message_dict, template_dict=False, mail_from=ADBOARD_SETTINGS['mail_from'], attach=False):

    if not isinstance(mail_to, list):
        mail_to = [mail_to]

    if template_dict:
        txt = loader.get_template(template_dict['txt'])
        html = loader.get_template(template_dict['html'])
        c = Context(message_dict or None)
        message_txt = txt.render(c)
        message_html = html.render(c)
        msg = EmailMultiAlternatives(subj, message_txt, mail_from, mail_to)
        msg.attach_alternative(message_html, "text/html")
    else:
        msg = EmailMessage(subj, message_dict, mail_from, mail_to)
    if attach:
        for one in attach:
            msg.attach(one[0], one[1], one[2])
    msg.send()

def contacts(request):
    from choices import ContactCategoryChoices

    if request.method == 'POST':
        form = ContactForm(request.POST, request.FILES)
        if form.is_valid():
            data = {
                'email': form.cleaned_data.get('email'),
                'subject': form.cleaned_data.get('subject'),
                'message': form.cleaned_data.get('message'),
                'category': int(form.cleaned_data.get('category'))
            }

            recievers = (
                (#recievers)

            emailList = list(dict(recievers)[data.get('category')])

            attaches = [(upload.name, upload.read(), upload.content_type) for upload in
                        [form.cleaned_data[attach] for attach in request.FILES]]
            send_mail(emailList, u'Письмо со страницы контактов', data, {
                'txt': 'home/contacts/txt.html',
                'html': 'home/contacts/html.html',
            }, mail_from=settings.SENDER_EMAIL, attach=attaches)
            messages.add_message(request, messages.SUCCESS, u'Сообщение отправлено')
            form = ContactForm()
    else:
        form = ContactForm()
    return render_to_response('home/contacts/index.html', {'form': form}, context_instance=RequestContext(request))

If I add some files - they appears as 'noname' in attachements… Not only gmail. Django 1.3.1, utf-8 named files. Is there any chance to make it work without converting to ascii? As long as I can't convert it due to save original filenames is very important.

Imgur

Update:
Meanwhile header seems legit:

Headers

like image 519
mktums Avatar asked Mar 19 '13 10:03

mktums


2 Answers

Ok. Figured it out with following code:

import mimetypes
from email import encoders
from email.header import Header
from email.mime.base import MIMEBase
from django.core.mail.message import DEFAULT_ATTACHMENT_MIME_TYPE

class EmailMultiAlternativesWithEncoding(EmailMultiAlternatives):
    def _create_attachment(self, filename, content, mimetype=None):
        """
        Converts the filename, content, mimetype triple into a MIME attachment
        object. Use self.encoding when handling text attachments.
        """
        if mimetype is None:
            mimetype, _ = mimetypes.guess_type(filename)
            if mimetype is None:
                mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
        basetype, subtype = mimetype.split('/', 1)
        if basetype == 'text':
            encoding = self.encoding or settings.DEFAULT_CHARSET
            attachment = SafeMIMEText(smart_str(content,
                settings.DEFAULT_CHARSET), subtype, encoding)
        else:
            # Encode non-text attachments with base64.
            attachment = MIMEBase(basetype, subtype)
            attachment.set_payload(content)
            encoders.encode_base64(attachment)
        if filename:
            try:
                filename = filename.encode('ascii')
            except UnicodeEncodeError:
                filename = Header(filename, 'utf-8').encode()    # This is the important line
            attachment.add_header('Content-Disposition', 'attachment',
                                   filename=filename)
        return attachment
like image 50
mktums Avatar answered Sep 24 '22 13:09

mktums


As per the fix made in ticket 14964:

from django.core.mail import EmailMultiAlternatives
import mimetypes
from django.core.mail.message import DEFAULT_ATTACHMENT_MIME_TYPE

class UnicodeEmailMultiAlternatives(EmailMultiAlternatives):

    def _create_attachment(self, filename, content, mimetype=None):
        """
        Overrides EmailMultiAlternatives method to allow UTF8 filenames as attachments.
        """
        if mimetype is None:
            mimetype, _ = mimetypes.guess_type(filename)
            if mimetype is None:
                mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
        attachment = self._create_mime_attachment(content, mimetype)
        if filename:
            try:
                filename = filename.encode('ascii')
            except UnicodeEncodeError:
                filename = ('utf-8', '', filename.encode('utf-8'))
            attachment.add_header('Content-Disposition', 'attachment',
                                  filename=filename)
        return attachment
like image 25
laffuste Avatar answered Sep 22 '22 13:09

laffuste