Recipe 13.6. Bundling Files in a MIME Message
Credit: Matthew Dixon Cowles, Hans Fangohr, John
Pywtorak
Problem
You want to create a multipart MIME
(Multipurpose Internet Mail Extensions) message that includes all
files in the current directory.
Solution
If you often deal with
composing or parsing mail messages, or mail-like messages such as
Usenet news posts, the Python Standard Library
email package gives you very powerful tools to
work with. Here is a module that uses email to
solve the task posed in the
"Problem":
#!/usr/bin/env python
import base64, quopri
import mimetypes, email.Generator, email.Message
import cStringIO, os
# sample addresses
toAddr = "example@example.com"
fromAddr = "example@example.com"
outputFile = "dirContentsMail"
def main( ):
mainMsg = email.Message.Message( )
mainMsg["To"] = toAddr
mainMsg["From"] = fromAddr
mainMsg["Subject"] = "Directory contents"
mainMsg["Mime-version"] = "1.0"
mainMsg["Content-type"] = "Multipart/mixed"
mainMsg.preamble = "Mime message\n"
mainMsg.epilogue = " # to ensure that message ends with newline
# Get names of plain files (not subdirectories or special files)
fileNames = [f for f in os.listdir(os.curdir) if os.path.isfile(f)]
for fileName in fileNames:
contentType, ignored = mimetypes.guess_type(fileName)
if contentType is None: # If no guess, use generic opaque type
contentType = "application/octet-stream"
contentsEncoded = cStringIO.StringIO( )
f = open(fileName, "rb")
mainType = contentType[:contentType.find("/")]
if mainType=="text":
cte = "quoted-printable"
quopri.encode(f, contentsEncoded, 1) # 1 to also encode tabs
else:
cte = "base64"
base64.encode(f, contentsEncoded)
f.close( )
subMsg = email.Message.Message( )
subMsg.add_header("Content-type", contentType, name=fileName)
subMsg.add_header("Content-transfer-encoding", cte)
subMsg.set_payload(contentsEncoded.getvalue( ))
contentsEncoded.close( )
mainMsg.attach(subMsg)
f = open(outputFile, "wb")
g = email.Generator.Generator(f)
g.flatten(mainMsg)
f.close( )
return None
if _ _name_ _=="_ _main_ _":
main( )
Discussion
The email package makes manipulating MIME messages
a snap. The Python Standard Library also offers other older modules
that can serve many of the same purposes, but I suggest you look into
email as an alternative to all such other modules.
email requires some study because it is a very
functionally rich package, but it will amply repay the time you spend
studying it.MIME is the Internet standard for sending files and non-ASCII data by
email. The standard is specified in RFCs 2045-2049. A few points are
especially worth keeping in mind:
- The original specification for the format of an email (RFC 822)
didn't allow for non-ASCII characters and had no
provision for attaching or enclosing a file along with a text
message. Therefore, not surprisingly, MIME messages are very common
these days. - Messages that follow the MIME standard are backward compatible with
ordinary RFC 822 (now RFC 2822) messages. An old mail reader
(technically, an MUA) that doesn't understand the
MIME specification will probably not be able to display a MIME
message in a way that's useful to the user, but the
message will still be legal and therefore shouldn't
cause unexpected behavior. - An RFC 2822 message consists of a set of headers, a blank line, and a
body. MIME handles attachments and other multipart documents by
specifying a format for the message's body. In
multipart MIME messages, the body is divided into submessages, each
of which has a set of headers, a blank line, and a body. Generally,
each submessage is referred to as a MIME part, and parts may nest
recursively. - MIME parts (whether or not in a multipart message) that contain
characters outside of the strict US-ASCII range are encoded as either
base-64 or quoted-printable data, so that the resulting mail message
contains only ordinary ASCII characters. Data can be encoded with
either method, but, generally, only data that has few non-ASCII
characters (basically text, possibly with a few extra characters
outside of the ASCII range, such as national characters in Latin-1
and similar codes) is worth encoding as quoted-printable, because
even without decoding it may be readable. If the data is essentially
binary, with all bytes being equally likely, base-64 encoding is more
compact.
Not surprisingly, given all of these issues, manipulating MIME
messages is often considered to be a nuisance. In the old times, back
before Python 2.2, the standard library's modules
for dealing with MIME messages were quite useful but rather
miscellaneous. In particular, putting MIME messages together and
taking them apart required two distinct approaches. The
email package, which was added in Python 2.2,
unified and simplified these two related jobs.
See Also
Recipe 13.7 shows how the
email package can be used to unpack a MIME
message; documentation for the standard library modules
email, mimetypes,
base64, quopri, and
cStringIO in the Library
Reference and Python in a
Nutshell.