19.9 Multipart Messages and File Attachments
The way all the different text and
binary file types are encoded into raw text that can be passed
through 7-bit email gateways is fairly ingenious and rather detailed.
Fortunately, the JavaMail API shields you from those details,
interesting as they are. To send a multipart message using the
JavaMail API, all you have to do is add the parts to a
MimeMultipart object, then pass that object to the
Message's setContent(
) method. To receive a multipart message, you simply
process each of the parts individually.Most of the
methods for building and deconstructing multipart messages are in the
abstract javax.mail.Multipart class:
public abstract class Multipart extends ObjectHowever, since this class is abstract, you'll
generally start with a
javax.mail.internet.MimeMultipart object instead:
public class MimeMultipart extends MultipartEach part you add to a Multipart is an instance of
the abstract javax.mail.BodyPart class that
implements the Part interface of the last section:
public abstract class BodyPart extends Object implements PartIn Internet email, the concrete subclass of
BodyPart you'll use is
javax.mail.internet.MimeBodyPart:
public class MimeBodyPart extends BodyPart implements MimePartMost of the methods you need in the MimeBodyPart
and BodyPart classes are the ones
you're already familiar with from the
Part interface, methods such as
setContent( ) and setDataHandler(
). There are also three methods to read the contents of a
Multipart object:
public String getContentType( )The getContentType()
public int getCount( ) throws MessagingException
public BodyPart getBodyPart(int index)
throws IndexOutOfBoundsException, MessagingException
method returns the MIME media type of the entire
Multipart, which is typically something like
multipart/mixed or
multipart/alternative. This is not the same as the
MIME types of the individual parts, which are something like
text/plain or image/gif.The getCount( ) method returns the
number of parts in this Multipart. The
getBodyPart( ) method returns a particular part.
Parts are numbered starting at 0, like the components of an array.
Example 19-11, AllHeaderClient. However,
Example 19-12 adds the necessary code to handle the
body of the message. If the message is a single-part message,
it's simply printed on
System.out. However, if the message has multiple
parts, each part is handled separately. If the part has a multipart
content type itself, processMultipart() is called recursively. If the part has
no filename, does not have the disposition
Part.ATTACHMENT, and has MIME type
text/plain, it's assumed to be an
inline message and is printed on System.out.
Otherwise, it's assumed to be an attachment and is
saved into an appropriate file. If necessary, the static
File.createTempFile( ) method generates a
reasonable name for the file.
Example 19-12. A mail client that handles multipart messages with attached files
import javax.mail.*;You can also get a part from a multipart message by passing an
import javax.mail.internet.*;
import java.util.*;
import java.io.*;
public class AllPartsClient {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println(
"Usage: java AllPartsClient protocol://username@host:port/foldername");
return;
}
URLName server = new URLName(args[0]);
try {
Session session = Session.getDefaultInstance(new Properties( ),
new MailAuthenticator(server.getUsername( )));
// Connect to the server and open the folder
Folder folder = session.getFolder(server);
if (folder == null) {
System.out.println("Folder " + server.getFile( ) + " not found.");
System.exit(1);
}
folder.open(Folder.READ_ONLY);
// Get the messages from the server
Message[] messages = folder.getMessages( );
for (int i = 0; i < messages.length; i++) {
System.out.println("------------ Message " + (i+1)
+ " ------------");
// Print message headers
Enumeration headers = messages[i].getAllHeaders( );
while (headers.hasMoreElements( )) {
Header h = (Header) headers.nextElement( );
System.out.println(h.getName( ) + ": " + h.getValue( ));
}
System.out.println( );
// Enumerate parts
Object body = messages[i].getContent( );
if (body instanceof Multipart) {
processMultipart((Multipart) body);
}
else { // ordinary message
processPart(messages[i]);
}
System.out.println( );
}
// Close the connection
// but don't remove the messages from the server
folder.close(false);
}
catch (Exception ex) {
ex.printStackTrace( );
}
// Since we may have brought up a GUI to authenticate,
// we can't rely on returning from main( ) to exit
System.exit(0);
}
public static void processMultipart(Multipart mp)
throws MessagingException {
for (int i = 0; i < mp.getCount( ); i++) {
processPart(mp.getBodyPart(i));
}
}
public static void processPart(Part p) {
try {
String fileName = p.getFileName( );
String disposition = p.getDisposition( );
String contentType = p.getContentType( );
if (contentType.toLowerCase( ).startsWith("multipart/")) {
processMultipart((Multipart) p.getContent( ) );
}
else if (fileName == null
&& (Part.ATTACHMENT.equalsIgnoreCase(disposition)
|| !contentType.equalsIgnoreCase("text/plain"))) {
// pick a random file name. This requires Java 1.2 or later.
fileName = File.createTempFile("attachment", ".txt").getName( );
}
if (fileName == null) { // likely inline
p.writeTo(System.out);
}
else {
File f = new File(fileName);
// find a file that does not yet exist
for (int i = 1; f.exists( ); i++) {
String newName = fileName + " " + i;
f = new File(newName);
}
OutputStream out = new BufferedOutputStream(new FileOutputStream(f));
// We can't just use p.writeTo( ) here because it doesn't
// decode the attachment. Instead we copy the input stream
// onto the output stream which does automatically decode
// Base-64, quoted printable, and a variety of other formats.
InputStream in = new BufferedInputStream(p.getInputStream( ));
int b;
while ((b = in.read( )) != -1) out.write(b);
out.flush( );
out.close( );
in.close( );
}
}
catch (Exception ex) {
System.err.println(e);
ex.printStackTrace( );
}
}
}
OutputStream to the
part's
writeTo( ) method:
public abstract void writeTo(OutputStream out)However, this differs from the approach taken in Example 19-12 in
throws IOException, MessagingException
that it does not decode the part before writing it. It leaves
whatever Base64, BinHex, or quoted-printable encoding the sender
applied to the attachment alone. Instead, it simply writes the raw
data.Attaching files (or other documents) to messages you send is more
complicated. To attach a file to a message, you first have to wrap
the data in a BodyPart object and add it to the
Multipart using one of the two
addBodyPart( ) methods:
public void addBodyPart(BodyPart part)The first variant simply appends the part to the end of the message.
throws IllegalWriteException, MessagingException
public void addBodyPart(BodyPart part, int index)
throws IllegalWriteException, MessagingException
The second variant adds the given part at the specified position. If
the position is greater than the number of parts in the message, the
part is simply added to the end. If it's added
somewhere in the middle, this may cause the positions of other parts
to change. If the message can't be changed, an
IllegalWriteException is thrown.The tricky part is creating the BodyPart object.
You first need to guess a reasonable MIME content type for the file
(text/plain and
application/octet-stream are the most common
types). Next, read the file and convert it into some class of Java
object. Then install a
javax.activation.DataHandler class that knows how
to convert your data class according to your chosen MIME type. Once
you've done all this, you can create a new
MimeBodyPart object and use the various methods of
the Part interface to set attributes such as the
filename and the content disposition.There are also two removeBodyPart() methods that delete a specified part
from the message, although these aren't as commonly
used:
public boolean removeBodyPart(BodyPart part)If the message can't be changed, an
throws IllegalWriteException, MessagingException
public void removeBodyPart(int index)
throws IndexOutOfBoundsException, MessagingException
IllegalWriteException is thrown. If the specified
index doesn't identify a part, an
IndexOutOfBoundsException is thrown. If the
specified part isn't present in the message, a
MessagingException is thrown.
• Table of Contents• Index• Reviews• Reader Reviews• Errata• AcademicJava Network Programming, 3rd EditionBy
Elliotte Rusty Harold Publisher: O'ReillyPub Date: October 2004ISBN: 0-596-00721-3Pages: 706
Thoroughly revised to cover all the 100+ significant updates
to Java Developers Kit (JDK) 1.5, Java Network
Programming is a complete introduction to
developing network programs (both applets and applications)
using Java, covering everything from networking fundamentals
to remote method invocation (RMI). It includes chapters on
TCP and UDP sockets, multicasting protocol and content
handlers, servlets, and the new I/O API. This is the
essential resource for any serious Java developer.