Home » commons-httpclient-3.1-src » org.apache.commons » httpclient » methods » multipart » [javadoc | source]
    1   /*
    2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/Part.java,v 1.16 2005/01/14 21:16:40 olegk Exp $
    3    * $Revision: 480424 $
    4    * $Date: 2006-11-29 06:56:49 +0100 (Wed, 29 Nov 2006) $
    5    *
    6    * ====================================================================
    7    *
    8    *  Licensed to the Apache Software Foundation (ASF) under one or more
    9    *  contributor license agreements.  See the NOTICE file distributed with
   10    *  this work for additional information regarding copyright ownership.
   11    *  The ASF licenses this file to You under the Apache License, Version 2.0
   12    *  (the "License"); you may not use this file except in compliance with
   13    *  the License.  You may obtain a copy of the License at
   14    *
   15    *      http://www.apache.org/licenses/LICENSE-2.0
   16    *
   17    *  Unless required by applicable law or agreed to in writing, software
   18    *  distributed under the License is distributed on an "AS IS" BASIS,
   19    *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   20    *  See the License for the specific language governing permissions and
   21    *  limitations under the License.
   22    * ====================================================================
   23    *
   24    * This software consists of voluntary contributions made by many
   25    * individuals on behalf of the Apache Software Foundation.  For more
   26    * information on the Apache Software Foundation, please see
   27    * <http://www.apache.org/>.
   28    *
   29    */
   30   
   31   package org.apache.commons.httpclient.methods.multipart;
   32   
   33   import java.io.ByteArrayOutputStream;
   34   import java.io.IOException;
   35   import java.io.OutputStream;
   36   
   37   import org.apache.commons.httpclient.util.EncodingUtil;
   38   import org.apache.commons.logging.Log;
   39   import org.apache.commons.logging.LogFactory;
   40   
   41   /**
   42    * Abstract class for one Part of a multipart post object.
   43    *
   44    * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
   45    * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
   46    * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
   47    * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
   48    * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
   49    *
   50    * @since 2.0
   51    */
   52   public abstract class Part {
   53   
   54       /** Log object for this class. */
   55       private static final Log LOG = LogFactory.getLog(Part.class);
   56   
   57       /** 
   58        * The boundary 
   59        * @deprecated use {@link org.apache.commons.httpclient.params.HttpMethodParams#MULTIPART_BOUNDARY}
   60        */
   61       protected static final String BOUNDARY = "----------------314159265358979323846";
   62       
   63       /** 
   64        * The boundary as a byte array.
   65        * @deprecated
   66        */
   67       protected static final byte[] BOUNDARY_BYTES = EncodingUtil.getAsciiBytes(BOUNDARY);
   68   
   69       /**
   70        * The default boundary to be used if {@link #setBoundaryBytes(byte[])) has not
   71        * been called.
   72        */
   73       private static final byte[] DEFAULT_BOUNDARY_BYTES = BOUNDARY_BYTES;    
   74       
   75       /** Carriage return/linefeed */
   76       protected static final String CRLF = "\r\n";
   77       
   78       /** Carriage return/linefeed as a byte array */
   79       protected static final byte[] CRLF_BYTES = EncodingUtil.getAsciiBytes(CRLF);
   80       
   81       /** Content dispostion characters */
   82       protected static final String QUOTE = "\"";
   83       
   84       /** Content dispostion as a byte array */
   85       protected static final byte[] QUOTE_BYTES = 
   86         EncodingUtil.getAsciiBytes(QUOTE);
   87   
   88       /** Extra characters */
   89       protected static final String EXTRA = "--";
   90       
   91       /** Extra characters as a byte array */
   92       protected static final byte[] EXTRA_BYTES = 
   93         EncodingUtil.getAsciiBytes(EXTRA);
   94       
   95       /** Content dispostion characters */
   96       protected static final String CONTENT_DISPOSITION = "Content-Disposition: form-data; name=";
   97       
   98       /** Content dispostion as a byte array */
   99       protected static final byte[] CONTENT_DISPOSITION_BYTES = 
  100         EncodingUtil.getAsciiBytes(CONTENT_DISPOSITION);
  101   
  102       /** Content type header */
  103       protected static final String CONTENT_TYPE = "Content-Type: ";
  104   
  105       /** Content type header as a byte array */
  106       protected static final byte[] CONTENT_TYPE_BYTES = 
  107         EncodingUtil.getAsciiBytes(CONTENT_TYPE);
  108   
  109       /** Content charset */
  110       protected static final String CHARSET = "; charset=";
  111   
  112       /** Content charset as a byte array */
  113       protected static final byte[] CHARSET_BYTES = 
  114         EncodingUtil.getAsciiBytes(CHARSET);
  115   
  116       /** Content type header */
  117       protected static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding: ";
  118   
  119       /** Content type header as a byte array */
  120       protected static final byte[] CONTENT_TRANSFER_ENCODING_BYTES = 
  121         EncodingUtil.getAsciiBytes(CONTENT_TRANSFER_ENCODING);
  122   
  123       /**
  124        * Return the boundary string.
  125        * @return the boundary string
  126        * @deprecated uses a constant string. Rather use {@link #getPartBoundary}
  127        */
  128       public static String getBoundary() {
  129           return BOUNDARY;
  130       }
  131   
  132       /**
  133        * The ASCII bytes to use as the multipart boundary.
  134        */
  135       private byte[] boundaryBytes;
  136       
  137       /**
  138        * Return the name of this part.
  139        * @return The name.
  140        */
  141       public abstract String getName();
  142       
  143       /**
  144        * Returns the content type of this part.
  145        * @return the content type, or <code>null</code> to exclude the content type header
  146        */
  147       public abstract String getContentType();
  148   
  149       /**
  150        * Return the character encoding of this part.
  151        * @return the character encoding, or <code>null</code> to exclude the character 
  152        * encoding header
  153        */
  154       public abstract String getCharSet();
  155   
  156       /**
  157        * Return the transfer encoding of this part.
  158        * @return the transfer encoding, or <code>null</code> to exclude the transfer encoding header
  159        */
  160       public abstract String getTransferEncoding();
  161   
  162       /**
  163        * Gets the part boundary to be used.
  164        * @return the part boundary as an array of bytes.
  165        * 
  166        * @since 3.0
  167        */
  168       protected byte[] getPartBoundary() {
  169           if (boundaryBytes == null) {
  170               // custom boundary bytes have not been set, use the default.
  171               return DEFAULT_BOUNDARY_BYTES;
  172           } else {
  173               return boundaryBytes;            
  174           }
  175       }
  176       
  177       /**
  178        * Sets the part boundary.  Only meant to be used by 
  179        * {@link Part#sendParts(OutputStream, Part[], byte[])}
  180        * and {@link Part#getLengthOfParts(Part[], byte[])}
  181        * @param boundaryBytes An array of ASCII bytes.
  182        * @since 3.0
  183        */
  184       void setPartBoundary(byte[] boundaryBytes) {
  185           this.boundaryBytes = boundaryBytes;
  186       }
  187       
  188       /**
  189        * Tests if this part can be sent more than once.
  190        * @return <code>true</code> if {@link #sendData(OutputStream)} can be successfully called 
  191        * more than once.
  192        * @since 3.0
  193        */
  194       public boolean isRepeatable() {
  195           return true;
  196       }
  197       
  198       /**
  199        * Write the start to the specified output stream
  200        * @param out The output stream
  201        * @throws IOException If an IO problem occurs.
  202        */
  203       protected void sendStart(OutputStream out) throws IOException {
  204           LOG.trace("enter sendStart(OutputStream out)");
  205           out.write(EXTRA_BYTES);
  206           out.write(getPartBoundary());
  207           out.write(CRLF_BYTES);
  208       }
  209       
  210       /**
  211        * Write the content disposition header to the specified output stream
  212        * 
  213        * @param out The output stream
  214        * @throws IOException If an IO problem occurs.
  215        */
  216       protected void sendDispositionHeader(OutputStream out) throws IOException {
  217           LOG.trace("enter sendDispositionHeader(OutputStream out)");
  218           out.write(CONTENT_DISPOSITION_BYTES);
  219           out.write(QUOTE_BYTES);
  220           out.write(EncodingUtil.getAsciiBytes(getName()));
  221           out.write(QUOTE_BYTES);
  222       }
  223       
  224       /**
  225        * Write the content type header to the specified output stream
  226        * @param out The output stream
  227        * @throws IOException If an IO problem occurs.
  228        */
  229        protected void sendContentTypeHeader(OutputStream out) throws IOException {
  230           LOG.trace("enter sendContentTypeHeader(OutputStream out)");
  231           String contentType = getContentType();
  232           if (contentType != null) {
  233               out.write(CRLF_BYTES);
  234               out.write(CONTENT_TYPE_BYTES);
  235               out.write(EncodingUtil.getAsciiBytes(contentType));
  236               String charSet = getCharSet();
  237               if (charSet != null) {
  238                   out.write(CHARSET_BYTES);
  239                   out.write(EncodingUtil.getAsciiBytes(charSet));
  240               }
  241           }
  242       }
  243   
  244       /**
  245        * Write the content transfer encoding header to the specified 
  246        * output stream
  247        * 
  248        * @param out The output stream
  249        * @throws IOException If an IO problem occurs.
  250        */
  251        protected void sendTransferEncodingHeader(OutputStream out) throws IOException {
  252           LOG.trace("enter sendTransferEncodingHeader(OutputStream out)");
  253           String transferEncoding = getTransferEncoding();
  254           if (transferEncoding != null) {
  255               out.write(CRLF_BYTES);
  256               out.write(CONTENT_TRANSFER_ENCODING_BYTES);
  257               out.write(EncodingUtil.getAsciiBytes(transferEncoding));
  258           }
  259       }
  260   
  261       /**
  262        * Write the end of the header to the output stream
  263        * @param out The output stream
  264        * @throws IOException If an IO problem occurs.
  265        */
  266       protected void sendEndOfHeader(OutputStream out) throws IOException {
  267           LOG.trace("enter sendEndOfHeader(OutputStream out)");
  268           out.write(CRLF_BYTES);
  269           out.write(CRLF_BYTES);
  270       }
  271       
  272       /**
  273        * Write the data to the specified output stream
  274        * @param out The output stream
  275        * @throws IOException If an IO problem occurs.
  276        */
  277       protected abstract void sendData(OutputStream out) throws IOException;
  278       
  279       /**
  280        * Return the length of the main content
  281        * 
  282        * @return long The length.
  283        * @throws IOException If an IO problem occurs
  284        */
  285       protected abstract long lengthOfData() throws IOException;
  286       
  287       /**
  288        * Write the end data to the output stream.
  289        * @param out The output stream
  290        * @throws IOException If an IO problem occurs.
  291        */
  292       protected void sendEnd(OutputStream out) throws IOException {
  293           LOG.trace("enter sendEnd(OutputStream out)");
  294           out.write(CRLF_BYTES);
  295       }
  296       
  297       /**
  298        * Write all the data to the output stream.
  299        * If you override this method make sure to override 
  300        * #length() as well
  301        * 
  302        * @param out The output stream
  303        * @throws IOException If an IO problem occurs.
  304        */
  305       public void send(OutputStream out) throws IOException {
  306           LOG.trace("enter send(OutputStream out)");
  307           sendStart(out);
  308           sendDispositionHeader(out);
  309           sendContentTypeHeader(out);
  310           sendTransferEncodingHeader(out);
  311           sendEndOfHeader(out);
  312           sendData(out);
  313           sendEnd(out);
  314       }
  315   
  316   
  317       /**
  318        * Return the full length of all the data.
  319        * If you override this method make sure to override 
  320        * #send(OutputStream) as well
  321        * 
  322        * @return long The length.
  323        * @throws IOException If an IO problem occurs
  324        */
  325       public long length() throws IOException {
  326           LOG.trace("enter length()");
  327           if (lengthOfData() < 0) {
  328               return -1;
  329           }
  330           ByteArrayOutputStream overhead = new ByteArrayOutputStream();
  331           sendStart(overhead);
  332           sendDispositionHeader(overhead);
  333           sendContentTypeHeader(overhead);
  334           sendTransferEncodingHeader(overhead);
  335           sendEndOfHeader(overhead);
  336           sendEnd(overhead);
  337           return overhead.size() + lengthOfData();
  338       }
  339   
  340       /**
  341        * Return a string representation of this object.
  342        * @return A string representation of this object.
  343        * @see java.lang.Object#toString()
  344        */    
  345       public String toString() {
  346           return this.getName();
  347       }
  348   
  349       /**
  350        * Write all parts and the last boundary to the specified output stream.
  351        * 
  352        * @param out The stream to write to.
  353        * @param parts The parts to write.
  354        * 
  355        * @throws IOException If an I/O error occurs while writing the parts.
  356        */
  357       public static void sendParts(OutputStream out, final Part[] parts)
  358           throws IOException {
  359           sendParts(out, parts, DEFAULT_BOUNDARY_BYTES);
  360       }
  361   
  362       /**
  363        * Write all parts and the last boundary to the specified output stream.
  364        * 
  365        * @param out The stream to write to.
  366        * @param parts The parts to write.
  367        * @param partBoundary The ASCII bytes to use as the part boundary.
  368        * 
  369        * @throws IOException If an I/O error occurs while writing the parts.
  370        * 
  371        * @since 3.0
  372        */
  373       public static void sendParts(OutputStream out, Part[] parts, byte[] partBoundary)
  374           throws IOException {
  375           
  376           if (parts == null) {
  377               throw new IllegalArgumentException("Parts may not be null"); 
  378           }
  379           if (partBoundary == null || partBoundary.length == 0) {
  380               throw new IllegalArgumentException("partBoundary may not be empty");
  381           }
  382           for (int i = 0; i < parts.length; i++) {
  383               // set the part boundary before the part is sent
  384               parts[i].setPartBoundary(partBoundary);
  385               parts[i].send(out);
  386           }
  387           out.write(EXTRA_BYTES);
  388           out.write(partBoundary);
  389           out.write(EXTRA_BYTES);
  390           out.write(CRLF_BYTES);
  391       }
  392       
  393       /**
  394        * Return the total sum of all parts and that of the last boundary
  395        * 
  396        * @param parts The parts.
  397        * @return The total length
  398        * 
  399        * @throws IOException If an I/O error occurs while writing the parts.
  400        */
  401       public static long getLengthOfParts(Part[] parts)
  402       throws IOException {
  403           return getLengthOfParts(parts, DEFAULT_BOUNDARY_BYTES);
  404       }
  405       
  406       /**
  407        * Gets the length of the multipart message including the given parts.
  408        * 
  409        * @param parts The parts.
  410        * @param partBoundary The ASCII bytes to use as the part boundary.
  411        * @return The total length
  412        * 
  413        * @throws IOException If an I/O error occurs while writing the parts.
  414        * 
  415        * @since 3.0
  416        */
  417       public static long getLengthOfParts(Part[] parts, byte[] partBoundary) throws IOException {
  418           LOG.trace("getLengthOfParts(Parts[])");
  419           if (parts == null) {
  420               throw new IllegalArgumentException("Parts may not be null"); 
  421           }
  422           long total = 0;
  423           for (int i = 0; i < parts.length; i++) {
  424               // set the part boundary before we calculate the part's length
  425               parts[i].setPartBoundary(partBoundary);
  426               long l = parts[i].length();
  427               if (l < 0) {
  428                   return -1;
  429               }
  430               total += l;
  431           }
  432           total += EXTRA_BYTES.length;
  433           total += partBoundary.length;
  434           total += EXTRA_BYTES.length;
  435           total += CRLF_BYTES.length;
  436           return total;
  437       }        
  438   }

Save This Page
Home » commons-httpclient-3.1-src » org.apache.commons » httpclient » methods » multipart » [javadoc | source]