View Javadoc

1   /*
2    * $HeadURL: https://svn.apache.org/repos/asf/httpcomponents/oac.hc3x/trunk/src/java/org/apache/commons/httpclient/methods/MultipartPostMethod.java $
3    * $Revision$
4    * $Date$
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;
32  
33  import java.io.File;
34  import java.io.FileNotFoundException;
35  import java.io.IOException;
36  import java.io.OutputStream;
37  import java.util.ArrayList;
38  import java.util.List;
39  
40  import org.apache.commons.httpclient.HttpConnection;
41  import org.apache.commons.httpclient.HttpException;
42  import org.apache.commons.httpclient.HttpState;
43  import org.apache.commons.httpclient.methods.multipart.FilePart;
44  import org.apache.commons.httpclient.methods.multipart.Part;
45  import org.apache.commons.httpclient.methods.multipart.StringPart;
46  import org.apache.commons.logging.Log;
47  import org.apache.commons.logging.LogFactory;
48  
49  /***
50   * Implements the HTTP multipart POST method.
51   * <p>
52   * The HTTP multipart POST method is defined in section 3.3 of
53   * <a href="http://www.ietf.org/rfc/rfc1867.txt">RFC1867</a>:
54   * <blockquote>
55   * The media-type multipart/form-data follows the rules of all multipart
56   * MIME data streams as outlined in RFC 1521. The multipart/form-data contains 
57   * a series of parts. Each part is expected to contain a content-disposition 
58   * header where the value is "form-data" and a name attribute specifies 
59   * the field name within the form, e.g., 'content-disposition: form-data; 
60   * name="xxxxx"', where xxxxx is the field name corresponding to that field.
61   * Field names originally in non-ASCII character sets may be encoded using 
62   * the method outlined in RFC 1522.
63   * </blockquote>
64   * </p>
65   * <p>
66   *
67   * @author <a href="mailto:mattalbright@yahoo.com">Matthew Albright</a>
68   * @author <a href="mailto:jsdever@apache.org">Jeff Dever</a>
69   * @author <a href="mailto:adrian@ephox.com">Adrian Sutton</a>
70   * @author <a href="mailto:mdiggory@latte.harvard.edu">Mark Diggory</a>
71   * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
72   * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a>
73   *
74   * @since 2.0
75   * 
76   * @deprecated Use {@link org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity}
77   * in conjunction with {@link org.apache.commons.httpclient.methods.PostMethod} instead.
78   */
79  public class MultipartPostMethod extends ExpectContinueMethod {
80  
81      /*** The Content-Type for multipart/form-data. */
82      public static final String MULTIPART_FORM_CONTENT_TYPE = 
83          "multipart/form-data";
84  
85      /*** Log object for this class. */
86      private static final Log LOG = LogFactory.getLog(MultipartPostMethod.class);
87  
88      /*** The parameters for this method */
89      private final List parameters = new ArrayList();
90  
91      /***
92       * No-arg constructor.
93       */
94      public MultipartPostMethod() {
95          super();
96      }
97  
98      /***
99       * Constructor specifying a URI.
100      *
101      * @param uri either an absolute or relative URI
102      */
103     public MultipartPostMethod(String uri) {
104         super(uri);
105     }
106 
107     /***
108      * Returns <tt>true</tt> 
109      * 
110      * @return <tt>true</tt>
111      * 
112      * @since 2.0beta1
113      */
114     protected boolean hasRequestContent() {
115         return true;
116     }
117 
118     /***
119      * Returns <tt>"POST"</tt>.
120      * @return <tt>"POST"</tt>
121      */
122     public String getName() {
123         return "POST";
124     }
125 
126     /***
127      * Adds a text field part
128      * 
129      * @param parameterName The name of the parameter.
130      * @param parameterValue The value of the parameter.
131      */
132     public void addParameter(String parameterName, String parameterValue) {
133         LOG.trace("enter addParameter(String parameterName, String parameterValue)");
134         Part param = new StringPart(parameterName, parameterValue);
135         parameters.add(param);
136     }
137 
138     /***
139      * Adds a binary file part
140      * 
141      * @param parameterName The name of the parameter
142      * @param parameterFile The name of the file.
143      * @throws FileNotFoundException If the file cannot be found.
144      */
145     public void addParameter(String parameterName, File parameterFile) 
146     throws FileNotFoundException {
147         LOG.trace("enter MultipartPostMethod.addParameter(String parameterName, "
148             + "File parameterFile)");
149         Part param = new FilePart(parameterName, parameterFile);
150         parameters.add(param);
151     }
152 
153     /***
154      * Adds a binary file part with the given file name
155      * 
156      * @param parameterName The name of the parameter
157      * @param fileName The file name
158      * @param parameterFile The file
159      * @throws FileNotFoundException If the file cannot be found.
160      */
161     public void addParameter(String parameterName, String fileName, File parameterFile) 
162     throws FileNotFoundException {
163         LOG.trace("enter MultipartPostMethod.addParameter(String parameterName, "
164             + "String fileName, File parameterFile)");
165         Part param = new FilePart(parameterName, fileName, parameterFile);
166         parameters.add(param);
167     }
168         
169     /***
170      * Adds a part.
171      * 
172      * @param part The part to add.
173      */
174     public void addPart (final Part part) {
175         LOG.trace("enter addPart(Part part)");
176         parameters.add(part);
177     }
178 
179     /***
180      * Returns all parts.
181      * 
182      * @return an array of containing all parts
183      */
184     public Part[] getParts() {
185         return (Part[]) parameters.toArray(new Part[parameters.size()]);
186     }
187 
188     /***
189      * Adds a <tt>Content-Length</tt> request header, as long as no 
190      * <tt>Content-Length</tt> request header already exists.
191      *
192      * @param state current state of http requests
193      * @param conn the connection to use for I/O
194      *
195      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
196      *                     can be recovered from.
197      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
198      *                    cannot be recovered from.
199      * 
200      * @since 3.0
201      */
202     protected void addContentLengthRequestHeader(HttpState state,
203                                                  HttpConnection conn)
204     throws IOException, HttpException {
205         LOG.trace("enter EntityEnclosingMethod.addContentLengthRequestHeader("
206                   + "HttpState, HttpConnection)");
207 
208         if (getRequestHeader("Content-Length") == null) { 
209             long len = getRequestContentLength();
210             addRequestHeader("Content-Length", String.valueOf(len));
211         }
212         removeRequestHeader("Transfer-Encoding");
213     }
214 
215     /***
216      * Adds a <tt>Content-Type</tt> request header.
217      *
218      * @param state current state of http requests
219      * @param conn the connection to use for I/O
220      *
221      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
222      *                     can be recovered from.
223      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
224      *                    cannot be recovered from.
225      * 
226      * @since 3.0
227      */
228     protected void addContentTypeRequestHeader(HttpState state,
229                                                  HttpConnection conn)
230     throws IOException, HttpException {
231         LOG.trace("enter EntityEnclosingMethod.addContentTypeRequestHeader("
232                   + "HttpState, HttpConnection)");
233 
234         if (!parameters.isEmpty()) {
235             StringBuffer buffer = new StringBuffer(MULTIPART_FORM_CONTENT_TYPE);
236             if (Part.getBoundary() != null) {
237                 buffer.append("; boundary=");
238                 buffer.append(Part.getBoundary());
239             }
240             setRequestHeader("Content-Type", buffer.toString());
241         }
242     }
243 
244     /***
245      * Populates the request headers map to with additional 
246      * {@link org.apache.commons.httpclient.Header headers} to be submitted to 
247      * the given {@link HttpConnection}.
248      *
249      * <p>
250      * This implementation adds tt>Content-Length</tt> and <tt>Content-Type</tt>
251      * headers, when appropriate.
252      * </p>
253      *
254      * <p>
255      * Subclasses may want to override this method to to add additional
256      * headers, and may choose to invoke this implementation (via
257      * <tt>super</tt>) to add the "standard" headers.
258      * </p>
259      *
260      * @param state the {@link HttpState state} information associated with this method
261      * @param conn the {@link HttpConnection connection} used to execute
262      *        this HTTP method
263      *
264      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
265      *                     can be recovered from.
266      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
267      *                    cannot be recovered from.
268      *
269      * @see #writeRequestHeaders
270      */
271     protected void addRequestHeaders(HttpState state, HttpConnection conn) 
272     throws IOException, HttpException {
273         LOG.trace("enter MultipartPostMethod.addRequestHeaders(HttpState state, "
274             + "HttpConnection conn)");
275         super.addRequestHeaders(state, conn);
276         addContentLengthRequestHeader(state, conn);
277         addContentTypeRequestHeader(state, conn);
278     }
279 
280     /***
281      * Writes the request body to the given {@link HttpConnection connection}.
282      *
283      * @param state the {@link HttpState state} information associated with this method
284      * @param conn the {@link HttpConnection connection} used to execute
285      *        this HTTP method
286      *
287      * @return <tt>true</tt>
288      *
289      * @throws IOException if an I/O (transport) error occurs. Some transport exceptions
290      *                     can be recovered from.
291      * @throws HttpException  if a protocol exception occurs. Usually protocol exceptions 
292      *                    cannot be recovered from.
293      */
294     protected boolean writeRequestBody(HttpState state, HttpConnection conn) 
295     throws IOException, HttpException {
296         LOG.trace("enter MultipartPostMethod.writeRequestBody(HttpState state, "
297             + "HttpConnection conn)");
298         OutputStream out = conn.getRequestOutputStream();
299         Part.sendParts(out, getParts());
300         return true;
301     }
302 
303     /***
304      * <p>Return the length of the request body.</p>
305      *
306      * <p>Once this method has been invoked, the request parameters cannot be
307      * altered until the method is {@link #recycle recycled}.</p>
308      * 
309      * @return The request content length.
310      */
311     protected long getRequestContentLength() throws IOException {
312         LOG.trace("enter MultipartPostMethod.getRequestContentLength()");
313         return Part.getLengthOfParts(getParts());
314     }
315 
316 
317     /***
318      * Recycles the HTTP method so that it can be used again.
319      * Note that all of the instance variables will be reset
320      * once this method has been called. This method will also
321      * release the connection being used by this HTTP method.
322      * 
323      * @see #releaseConnection()
324      * 
325      * @deprecated no longer supported and will be removed in the future
326      *             version of HttpClient
327      */
328     public void recycle() {
329         LOG.trace("enter MultipartPostMethod.recycle()");
330         super.recycle();
331         parameters.clear();
332     }
333 }