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