I am trying to implement a mailer API with Serverless Framework and AWS Lambda. Plan is to send the mail details and attachment in a multipart/form-data POST request and then read the file and append it as an attachment.
def handler(event, context):
try:
body_file = BytesIO(event['body'].encode('utf-8'))
form, files = parse_into_field_storage(body_file, event['headers']['Content-Type'], body_file.getbuffer().nbytes)
mails = get_templated_mails(form, files)
send_mails(sender, mails)
except Exception as e:
print(e)
traceback.print_exc()
return { "statusCode": 500, "body": json.dumps({ 'message': "Mailer failed" })}
return { "statusCode": 200, "body": json.dumps({ 'message': 'Success' })}
The following code reads the multipart/form-data request body and parses it
def parse_into_field_storage(fp, ctype, clength):
fs = FieldStorage( fp=fp, environ={'REQUEST_METHOD': 'POST'}, headers={'content-type': ctype}, keep_blank_values=True )
form = {}
files = {}
for f in fs.list:
if f.filename:
files.setdefault(f.name, []).append(f)
else:
form.setdefault(f.name, []).append(f.value)
if len(files):
files = files['attachments']
return form, files
Below is the code to construct the mail, while the mail body looks fine, the attachment somehow is getting corrupted.
def get_templated_mails(form, files):
mails = []
sender = get_sender(form)
template = form['template'][0]
subject = form['subject'][0]
template_data = json.loads(form['template_data'][0])
print(template_data)
for data in template_data:
mail = MIMEMultipart()
mail['To'] = data['to']
mail['From'] = sender
mail['Subject'] = populate_data_to_string(data['subject_data'], subject)
body = MIMEText(populate_data_to_string(data['data'], template), 'html')
mail.attach(body)
mail = load_attachments(mail, files)
mails.append({'raw': base64.b64encode(mail.as_string().encode()).decode()})
return mails
def load_attachments(mail, files):
for file in files:
content_type, encoding = mimetypes.guess_type(file.filename)
print("Mime type ==============> ", content_type, encoding)
if content_type is None or encoding is not None:
content_type = 'application/octet-stream'
main_type, sub_type = content_type.split('/', 1)
print(main_type, sub_type)
if main_type == 'text':
attachment = MIMEText('r', _subtype=sub_type)
elif main_type == 'image':
attachment = MIMEImage('r', _subtype=sub_type)
elif main_type == 'audio':
attachment = MIMEAudio('r', _subtype=sub_type)
else:
attachment = MIMEBase(main_type, sub_type)
attachment.set_payload(base64.b64encode(file.fp.getbuffer()))
filename = file.filename
attachment.add_header('Content-Disposition', 'attachment', filename=filename)
mail.attach(attachment)
return mail
I have tried out a lot of things to fix encoding in load_attachments
, but nothing seems to help. I also tried to get the file content after the multipart parsing itself and convert it as a file, looks like its getting corrupted there itself.
What am I doing wrong here? Any clue would be helpful.