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 Object However, since this class is abstract, you'll generally start with a javax.mail.internet.MimeMultipart object instead: public class MimeMultipart extends Multipart Each 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 Part In Internet email, the concrete subclass of BodyPart you'll use is javax.mail.internet.MimeBodyPart : public class MimeBodyPart extends BodyPart implements MimePart Most 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( ) public int getCount( ) throws MessagingException public BodyPart getBodyPart(int index) throws IndexOutOfBoundsException, MessagingException The getContentType() 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-12 is very similar to 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.*; 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( ); } } } You can also get a part from a multipart message by passing an OutputStream to the part's writeTo( ) method: public abstract void writeTo(OutputStream out) throws IOException, MessagingException However, this differs from the approach taken in Example 19-12 in 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) throws IllegalWriteException, MessagingException public void addBodyPart(BodyPart part, int index) throws IllegalWriteException, MessagingException The first variant simply appends the part to the end of the message. 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) throws IllegalWriteException, MessagingException public void removeBodyPart(int index) throws IndexOutOfBoundsException, MessagingException If the message can't be changed, an 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. |