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 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.

Java Network Programming
Java Network Programming, Third Edition
ISBN: 0596007213
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net