View Javadoc

    1   /*
    2    * $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//httpclient/src/java/org/apache/commons/httpclient/methods/multipart/MultipartRequestEntity.java,v 1.1 2004/10/06 03:39:59 mbecke Exp $
    3    * $Revision: 502647 $
    4    * $Date: 2007-02-02 17:22:54 +0100 (Fri, 02 Feb 2007) $
    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  package org.apache.commons.httpclient.methods.multipart;
    31
    32  import java.io.IOException;
    33  import java.io.OutputStream;
    34  import java.util.Random;
    35
    36  import org.apache.commons.httpclient.methods.RequestEntity;
    37  import org.apache.commons.httpclient.params.HttpMethodParams;
    38  import org.apache.commons.httpclient.util.EncodingUtil;
    39  import org.apache.commons.logging.Log;
    40  import org.apache.commons.logging.LogFactory;
    41
    42  /**
    43   * Implements a request entity suitable for an HTTP multipart POST method.
    44   * <p>
    45   * The HTTP multipart POST method is defined in section 3.3 of
    46   * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>:
    47   * <blockquote>
    48   * The media-type multipart/form-data follows the rules of all multipart
    49   * MIME data streams as outlined in RFC 1521. The multipart/form-data contains
    50   * a series of parts. Each part is expected to contain a content-disposition
    51   * header where the value is "form-data" and a name attribute specifies
    52   * the field name within the form, e.g., 'content-disposition: form-data;
    53   * name="xxxxx"', where xxxxx is the field name corresponding to that field.
    54   * Field names originally in non-ASCII character sets may be encoded using
    55   * the method outlined in RFC 1522.
    56   * </blockquote>
    57   * </p>
    58   * <p>This entity is designed to be used in conjunction with the
    59   * {@link org.apache.commons.httpclient.methods.PostMethod post method} to provide
    60   * multipart posts.  Example usage:</p>
    61   * <pre>
    62   *  File f = new File("/path/fileToUpload.txt");
    63   *  PostMethod filePost = new PostMethod("http://host/some_path");
    64   *  Part[] parts = {
    65   *      new StringPart("param_name", "value"),
    66   *      new FilePart(f.getName(), f)
    67   *  };
    68   *  filePost.setRequestEntity(
    69   *      new MultipartRequestEntity(parts, filePost.getParams())
    70   *      );
    71   *  HttpClient client = new HttpClient();
    72   *  int status = client.executeMethod(filePost);
    73   * </pre>
    74   *
    75   * @since 3.0
    76   */
    77  public class MultipartRequestEntity implements RequestEntity {
    78
    79      private static final Log log = LogFactory.getLog(MultipartRequestEntity.class);
    80
    81      /** The Content-Type for multipart/form-data. */
    82      private static final String MULTIPART_FORM_CONTENT_TYPE = "multipart/form-data";
    83
    84      /**
    85       * The pool of ASCII chars to be used for generating a multipart boundary.
    86       */
    87      private static byte[] MULTIPART_CHARS = EncodingUtil.getAsciiBytes(
    88          "-_1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
    89
    90      /**
    91       * Generates a random multipart boundary string.
    92       * @return
    93       */
    94      private static byte[] generateMultipartBoundary() {
    95          Random rand = new Random();
    96          byte[] bytes = new byte[rand.nextInt(11) + 30]; // a random size from 30 to 40
    97          for (int i = 0; i < bytes.length; i++) {
    98              bytes[i] = MULTIPART_CHARS[rand.nextInt(MULTIPART_CHARS.length)];
    99          }
    100         return bytes;
    101     }
    102
    103     /** The MIME parts as set by the constructor */
    104     protected Part[] parts;
    105
    106     private byte[] multipartBoundary;
    107
    108     private HttpMethodParams params;
    109
    110     /**
    111      * Creates a new multipart entity containing the given parts.
    112      * @param parts The parts to include.
    113      * @param params The params of the HttpMethod using this entity.
    114      */
    115     public MultipartRequestEntity(Part[] parts, HttpMethodParams params) {
    116         if (parts == null) {
    117             throw new IllegalArgumentException("parts cannot be null");
    118         }
    119         if (params == null) {
    120             throw new IllegalArgumentException("params cannot be null");
    121         }
    122         this.parts = parts;
    123         this.params = params;
    124     }
    125
    126     /**
    127      * Returns the MIME boundary string that is used to demarcate boundaries of
    128      * this part. The first call to this method will implicitly create a new
    129      * boundary string. To create a boundary string first the
    130      * HttpMethodParams.MULTIPART_BOUNDARY parameter is considered. Otherwise
    131      * a random one is generated.
    132      *
    133      * @return The boundary string of this entity in ASCII encoding.
    134      */
    135     protected byte[] getMultipartBoundary() {
    136         if (multipartBoundary == null) {
    137             String temp = (String) params.getParameter(HttpMethodParams.MULTIPART_BOUNDARY);
    138             if (temp != null) {
    139                 multipartBoundary = EncodingUtil.getAsciiBytes(temp);
    140             } else {
    141                 multipartBoundary = generateMultipartBoundary();
    142             }
    143         }
    144         return multipartBoundary;
    145     }
    146
    147     /**
    148      * Returns <code>true</code> if all parts are repeatable, <code>false</code> otherwise.
    149      * @see org.apache.commons.httpclient.methods.RequestEntity#isRepeatable()
    150      */
    151     public boolean isRepeatable() {
    152         for (int i = 0; i < parts.length; i++) {
    153             if (!parts[i].isRepeatable()) {
    154                 return false;
    155             }
    156         }
    157         return true;
    158     }
    159
    160     /* (non-Javadoc)
    161      * @see org.apache.commons.httpclient.methods.RequestEntity#writeRequest(java.io.OutputStream)
    162      */
    163     public void writeRequest(OutputStream out) throws IOException {
    164         Part.sendParts(out, parts, getMultipartBoundary());
    165     }
    166
    167     /* (non-Javadoc)
    168      * @see org.apache.commons.httpclient.methods.RequestEntity#getContentLength()
    169      */
    170     public long getContentLength() {
    171         try {
    172             return Part.getLengthOfParts(parts, getMultipartBoundary());
    173         } catch (Exception e) {
    174             log.error("An exception occurred while getting the length of the parts", e);
    175             return 0;
    176         }
    177     }
    178
    179     /* (non-Javadoc)
    180      * @see org.apache.commons.httpclient.methods.RequestEntity#getContentType()
    181      */
    182     public String getContentType() {
    183         StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE);
    184         buffer.append("; boundary=");
    185         buffer.append(EncodingUtil.getAsciiString(getMultipartBoundary()));
    186         return buffer.toString();
    187     }
    188
    189 }