12

I have looked through many tutorials, as well as other question here on stack overflow, and the documentation and explanation are at minimum, just unexplained code. I would like to send a file that I already have zipped, and send it as an attachment. I have tried copy and pasting the code provided, but its not working, hence I cannot fix the problem.

So what I am asking is if anyone knows who to explain how smtplib as well as email and MIME libraries work together to send a file, more specifically, how to do it with a zip file. Any help would be appreciated.

This is the code that everyone refers to:

import smtplib
import zipfile
import tempfile
from email import encoders
from email.message import Message
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart    

def send_file_zipped(the_file, recipients, sender='you@you.com'):
    myzip = zipfile.ZipFile('file.zip', 'w')

    # Create the message
    themsg = MIMEMultipart()
    themsg['Subject'] = 'File %s' % the_file
    themsg['To'] = ', '.join(recipients)
    themsg['From'] = sender
    themsg.preamble = 'I am not using a MIME-aware mail reader.\n'
    msg = MIMEBase('application', 'zip')
    msg.set_payload(zf.read())
    encoders.encode_base64(msg)
    msg.add_header('Content-Disposition', 'attachment', 
               filename=the_file + '.zip')
    themsg.attach(msg)
    themsg = themsg.as_string()

    # send the message
    smtp = smtplib.SMTP()
    smtp.connect()
    smtp.sendmail(sender, recipients, themsg)
    smtp.close()

I suspect the issue is this code zips a file as well. I don't want to zip anything as I already have a zipped file I would like to send. In either case, this code is poorly documented as well as the python libraries themselves as they provide no insight on anything past img file and text files.

UPDATE: Error I am getting now. I have also updated what is in my file with the code above

Traceback (most recent call last):
File "/Users/Zeroe/Documents/python_hw/cgi-bin/zip_it.py", line 100, in <module>
send_file_zipped('hw5.zip', 'avaldez@oswego.edu')
File "/Users/Zeroe/Documents/python_hw/cgi-bin/zip_it.py", line 32, in send_file_zipped
msg.set_payload(myzip.read())
TypeError: read() takes at least 2 arguments (1 given)
Andy
  • 10,553
  • 21
  • 75
  • 125
  • 4
    *What* code is not working in *what* way? – Cameron May 06 '12 at 22:00
  • This is... the second code sample in the documentation for the `email` module. You'll have to give some specifics to get any answer that won't be essentially be a copy of that sample. – millimoose May 06 '12 at 22:02
  • Its not a copy... I am asking them to actually explain how it does what I need through a zip file. But I will post the same code everyone constantly refers to but does not explain... – Andy May 06 '12 at 22:05
  • So you want to ZIP a file, then send *that* as an attachment? Have you tried checking what part of that code breaks? Does it create the temporary ZIP file? What happens if you try to write the complete message into a file, does it look valid when you look at it in a text editor? Does anything at all arrive at the destination address? Will a test message without attachments arrive? Have you done any diagnostics of your own on this code you copy/pasted from the internet **at all**? – millimoose May 06 '12 at 22:15
  • Did you bother to read my question? "I don't want to zip anything as I already have a file zipped I would like to send" – Andy May 06 '12 at 22:17
  • 2
    Well then remove the part that would zip the file again from your code. – millimoose May 06 '12 at 22:19
  • Also, the indentation in what you posted is off. `send_file_zipped` would end after `zf.seek(0)`, before the email is sent. The code for sending the email would be run when the module is imported. And presumably throw an error, except you still haven't said what actually happens when you run it. – millimoose May 06 '12 at 22:21
  • 1
    Why do you think that sending an existing zip file is any different from sending an existing img or text file? – Daniel Roseman May 06 '12 at 22:37
  • 1
    What is `zf` in the `msg.set_payload(zf.read())` statement? – martineau May 06 '12 at 23:40
  • @martineau I edited what I put in my file per glglgl's advice – Andy May 06 '12 at 23:51
  • 1
    @andy: Even in your latest code, `zf` is undefined... – martineau May 07 '12 at 00:08
  • @Andy: Since you don't need to create a zip file, just open the existing one as a normal file -- you don't need to use the `zipfile` module at all -- see my commnent on @glglgl's answer. – martineau May 07 '12 at 00:11
  • @martineau So I tried the advice you gave me and it seemed to work. But not really sure what to do in the error I have now. Its, "socket.error: [Errno 61] Connection refused". In this case it seems it refuse to connect, but I have no idea why. Any idea how to allow me to connect? – Andy May 07 '12 at 01:27
  • @Andy: Hard to tell for sure esp without a stack trace, but you may have to log into the SMTP server before sending a message. See the comments by rbenegal at the end of the web page linked to in Jakub Oboza's answer to your question. – martineau May 07 '12 at 11:02
  • @Andy An aside: Do you expect the Python documentation to provide an extra explicit code sample for sending every possible attachment type? At some point you'll need to be able to make the connection between the `application/zip` MIME type and the `MIMEApplication` class (the documentation for which clearly states it's for sending files of the major type `application`) on your own. – millimoose May 07 '12 at 18:13

3 Answers3

12

I don't really see the problem. Just omit the part which creates the zip file and, instead, just load the zip file you have.

Essentially, this part here

msg = MIMEBase('application', 'zip')
msg.set_payload(zf.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment', 
               filename=the_file + '.zip')
themsg.attach(msg)

creates the attachment. The

msg.set_payload(zf.read())

sets, well, the payload of the attachment to what you read from the file zf (probably meaning zip file).

Just open your zip file beforehand and let this line read from it.

glglgl
  • 89,107
  • 13
  • 149
  • 217
  • I am doing that but it says the set_payload() takes 2 arguments and thats only one. – Andy May 06 '12 at 23:29
  • @Andy: The docs indicate the second argument, *charset*, is optional. – martineau May 06 '12 at 23:37
  • I will show you the error I am getting and the updated code like you told me to probably do. I appreciate the help. – Andy May 06 '12 at 23:45
  • 3
    @Andy: It's the `myzip.read()` that takes two arguments because `myzip` is an instance of the `ZipFile` class. I think the file you want to send should be the opened normally, i.e. something like `zf = open('file.zip', 'rb')` then `msg.setpayload(zf.read())`. – martineau May 07 '12 at 00:00
1

My answer uses shutil to zip a directory containing the attachments and then adds the .zip to the email.

# Importing Dependencies
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

import smtplib
import shutil

def Send_Email():

    # Create a multipart message
    msg = MIMEMultipart()
    Body = MIMEText( {Enter Email Body as str here} )
    # Add Headers
    msg['Subject'] = ''
    msg['From'] = ''
    msg['To'] = ''
    msg['CC'] = ''
    msg['BCC'] = ''

    # Add body to email
    msg.attach(Body)
    
    # Using Shutil to Zip a Directory
    dir_name = {Add Path of the Directory to be Zipped}
    output_filename = {Add Output Zip File Path}
    shutil.make_archive(output_filename, 'zip', dir_name)

    part = MIMEBase("application", "octet-stream")
    part.set_payload(open(output_filename + ".zip", "rb").read())
    encoders.encode_base64(part)
    part.add_header("Content-Disposition", "attachment; filename=\"%s.zip\"" % (output_filename))
    msg.attach(part)
The Singularity
  • 2,428
  • 3
  • 19
  • 48
0

I agree the email package is not well documented yet. I investigated it before and wrote a wrapper module that simplifies these kinds of tasks. For example, the following works:

from pycopia import ezmail

# Get the data
data = open("/usr/lib64/python2.7/test/zipdir.zip").read()

# Make a proper mime message object.
zipattachement = ezmail.MIMEApplication.MIMEApplication(data, "zip",
        filename="zipdir.zip")

# send it.
ezmail.ezmail(["Here is the zip file.", zipattachement],
        To="me@mydomain.com", From="me@mydomain.com", subject="zip send test")

And that's all you need once you have everything installed and configured. :-)

Keith
  • 42,110
  • 11
  • 57
  • 76
  • 4
    Since the OP doesn't have your wrapper module, I doubt this answer will be very useful to them... – martineau May 06 '12 at 23:48
  • @martineau It's open source, so it can be easily obtained. – Keith May 07 '12 at 00:07
  • 1
    Oh...not sure how one would know that though, at least from your answer alone. – martineau May 07 '12 at 00:35
  • I assume the OP will ask about it if interested. Or one could just google the package name. ;-) – Keith May 07 '12 at 01:02
  • Hi i have the same requirement, but after installing pycopia and when i tried "from pycopia import ezmail", the following message displayed ImportError: cannot import name ezmail, but when i tried import pycopia, that worked actually, y i am unable to import ezmail? – Shiva Krishna Bavandla Oct 22 '12 at 06:40
  • @Kouripm Could you post on the mailing list with full stack trace? Thanks! Will be easier to discuss it there than here. – Keith Oct 22 '12 at 06:55
  • k In [1]: from pycopia import ezmail --------------------------------------------------------------------------- ImportError Traceback (most recent call last) /home/local/user/src/pycopia/ in () ImportError: cannot import name ezmail – Shiva Krishna Bavandla Oct 22 '12 at 07:01