source: josm/trunk/src/org/openstreetmap/josm/io/MultiPartFormOutputStream.java@ 2626

Last change on this file since 2626 was 2626, checked in by jttt, 16 years ago

Fixed some of the warnings found by FindBugs

  • Property svn:eol-style set to native
File size: 14.7 KB
Line 
1/*
2Taken from forum.java.sun.com
3
4License
5
6Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved.
7Redistribution and use in source and binary forms, with or without modification,
8are permitted provided that the following conditions are met:
9
10 * Redistribution of source code must retain the above copyright notice, this list
11 of conditions and the following disclaimer.
12
13 * Redistribution in binary form must reproduce the above copyright notice, this
14 list of conditions and the following disclaimer in the documentation and/or other
15 materials provided with the distribution.
16
17Neither the name of Sun Microsystems, Inc. or the names of contributors may be used to
18endorse or promote products derived from this software without specific prior written
19permission.
20
21This software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR IMPLIED
22CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
23FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS,
24INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS
25A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT
26WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
27INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
28REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS
29SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
30
31You acknowledge that this software is not designed, licensed or intended for use in the
32design, construction, operation or maintenance of any nuclear facility.
33 */
34
35package org.openstreetmap.josm.io;
36
37import java.io.DataOutputStream;
38import java.io.FileInputStream;
39import java.io.InputStream;
40import java.io.OutputStream;
41import java.net.HttpURLConnection;
42import java.net.URL;
43import java.net.URLConnection;
44
45/**
46 * <code>MultiPartFormOutputStream</code> is used to write
47 * "multipart/form-data" to a <code>java.net.URLConnection</code> for
48 * POSTing. This is primarily for file uploading to HTTP servers.
49 *
50 * @since JDK1.3
51 */
52public class MultiPartFormOutputStream extends OsmConnection {
53 /**
54 * The line end characters.
55 */
56 private static final String NEWLINE = "\r\n";
57
58 /**
59 * The boundary prefix.
60 */
61 private static final String PREFIX = "--";
62
63 /**
64 * The output stream to write to.
65 */
66 private DataOutputStream out = null;
67
68 /**
69 * The multipart boundary string.
70 */
71 private String boundary = null;
72
73 /**
74 * Creates a new <code>MultiPartFormOutputStream</code> object using
75 * the specified output stream and boundary. The boundary is required
76 * to be created before using this method, as described in the
77 * description for the <code>getContentType(String)</code> method.
78 * The boundary is only checked for <code>null</code> or empty string,
79 * but it is recommended to be at least 6 characters. (Or use the
80 * static createBoundary() method to create one.)
81 *
82 * @param os the output stream
83 * @param boundary the boundary
84 * @see #createBoundary()
85 * @see #getContentType(String)
86 */
87 public MultiPartFormOutputStream(OutputStream os, String boundary) {
88 if(os == null)
89 throw new IllegalArgumentException("Output stream is required.");
90 if(boundary == null || boundary.length() == 0)
91 throw new IllegalArgumentException("Boundary stream is required.");
92 this.out = new DataOutputStream(os);
93 this.boundary = boundary;
94 initAuthentication();
95 }
96
97 /**
98 * Writes an boolean field value.
99 *
100 * @param name the field name (required)
101 * @param value the field value
102 * @throws java.io.IOException on input/output errors
103 */
104 public void writeField(String name, boolean value)
105 throws java.io.IOException {
106 writeField(name, Boolean.valueOf(value).toString());
107 }
108
109 /**
110 * Writes an double field value.
111 *
112 * @param name the field name (required)
113 * @param value the field value
114 * @throws java.io.IOException on input/output errors
115 */
116 public void writeField(String name, double value)
117 throws java.io.IOException {
118 writeField(name, Double.toString(value));
119 }
120
121 /**
122 * Writes an float field value.
123 *
124 * @param name the field name (required)
125 * @param value the field value
126 * @throws java.io.IOException on input/output errors
127 */
128 public void writeField(String name, float value)
129 throws java.io.IOException {
130 writeField(name, Float.toString(value));
131 }
132
133 /**
134 * Writes an long field value.
135 *
136 * @param name the field name (required)
137 * @param value the field value
138 * @throws java.io.IOException on input/output errors
139 */
140 public void writeField(String name, long value)
141 throws java.io.IOException {
142 writeField(name, Long.toString(value));
143 }
144
145 /**
146 * Writes an int field value.
147 *
148 * @param name the field name (required)
149 * @param value the field value
150 * @throws java.io.IOException on input/output errors
151 */
152 public void writeField(String name, int value)
153 throws java.io.IOException {
154 writeField(name, Integer.toString(value));
155 }
156
157 /**
158 * Writes an short field value.
159 *
160 * @param name the field name (required)
161 * @param value the field value
162 * @throws java.io.IOException on input/output errors
163 */
164 public void writeField(String name, short value)
165 throws java.io.IOException {
166 writeField(name, Short.toString(value));
167 }
168
169 /**
170 * Writes an char field value.
171 *
172 * @param name the field name (required)
173 * @param value the field value
174 * @throws java.io.IOException on input/output errors
175 */
176 public void writeField(String name, char value)
177 throws java.io.IOException {
178 writeField(name, Character.valueOf(value).toString());
179 }
180
181 /**
182 * Writes an string field value. If the value is null, an empty string
183 * is sent ("").
184 *
185 * @param name the field name (required)
186 * @param value the field value
187 * @throws java.io.IOException on input/output errors
188 */
189 public void writeField(String name, String value)
190 throws java.io.IOException {
191 if(name == null)
192 throw new IllegalArgumentException("Name cannot be null or empty.");
193 if(value == null) {
194 value = "";
195 }
196 /*
197 --boundary\r\n
198 Content-Disposition: form-data; name="<fieldName>"\r\n
199 \r\n
200 <value>\r\n
201 */
202 // write boundary
203 out.writeBytes(PREFIX);
204 out.writeBytes(boundary);
205 out.writeBytes(NEWLINE);
206 // write content header
207 out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"");
208 out.writeBytes(NEWLINE);
209 out.writeBytes(NEWLINE);
210 // write content
211 out.writeBytes(value);
212 out.writeBytes(NEWLINE);
213 out.flush();
214 }
215
216 /**
217 * Writes a file's contents. If the file is null, does not exists, or
218 * is a directory, a <code>java.lang.IllegalArgumentException</code>
219 * will be thrown.
220 *
221 * @param name the field name
222 * @param mimeType the file content type (optional, recommended)
223 * @param file the file (the file must exist)
224 * @throws java.io.IOException on input/output errors
225 */
226 public void writeFile(String name, String mimeType, java.io.File file)
227 throws java.io.IOException {
228 if(file == null)
229 throw new IllegalArgumentException("File cannot be null.");
230 if(!file.exists())
231 throw new IllegalArgumentException("File does not exist.");
232 if(file.isDirectory())
233 throw new IllegalArgumentException("File cannot be a directory.");
234 writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(file));
235 }
236
237 /**
238 * Writes a input stream's contents. If the input stream is null, a
239 * <code>java.lang.IllegalArgumentException</code> will be thrown.
240 *
241 * @param name the field name
242 * @param mimeType the file content type (optional, recommended)
243 * @param fileName the file name (required)
244 * @param is the input stream
245 * @throws java.io.IOException on input/output errors
246 */
247 public void writeFile(String name, String mimeType,
248 String fileName, InputStream is)
249 throws java.io.IOException {
250 if(is == null)
251 throw new IllegalArgumentException("Input stream cannot be null.");
252 if(fileName == null || fileName.length() == 0)
253 throw new IllegalArgumentException("File name cannot be null or empty.");
254 /*
255 --boundary\r\n
256 Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
257 Content-Type: <mime-type>\r\n
258 \r\n
259 <file-data>\r\n
260 */
261 // write boundary
262 out.writeBytes(PREFIX);
263 out.writeBytes(boundary);
264 out.writeBytes(NEWLINE);
265 // write content header
266 out.writeBytes("Content-Disposition: form-data; name=\"" + name +
267 "\"; filename=\"" + fileName + "\"");
268 out.writeBytes(NEWLINE);
269 if(mimeType != null) {
270 out.writeBytes("Content-Type: " + mimeType);
271 out.writeBytes(NEWLINE);
272 }
273 out.writeBytes(NEWLINE);
274 // write content
275 byte[] data = new byte[1024];
276 int r = 0;
277 while((r = is.read(data, 0, data.length)) != -1) {
278 out.write(data, 0, r);
279 }
280 // close input stream, but ignore any possible exception for it
281 try {
282 is.close();
283 } catch(Exception e) {}
284 out.writeBytes(NEWLINE);
285 out.flush();
286 }
287
288 /**
289 * Writes the given bytes. The bytes are assumed to be the contents
290 * of a file, and will be sent as such. If the data is null, a
291 * <code>java.lang.IllegalArgumentException</code> will be thrown.
292 *
293 * @param name the field name
294 * @param mimeType the file content type (optional, recommended)
295 * @param fileName the file name (required)
296 * @param data the file data
297 * @throws java.io.IOException on input/output errors
298 */
299 public void writeFile(String name, String mimeType,
300 String fileName, byte[] data)
301 throws java.io.IOException {
302 if(data == null)
303 throw new IllegalArgumentException("Data cannot be null.");
304 if(fileName == null || fileName.length() == 0)
305 throw new IllegalArgumentException("File name cannot be null or empty.");
306 /*
307 --boundary\r\n
308 Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
309 Content-Type: <mime-type>\r\n
310 \r\n
311 <file-data>\r\n
312 */
313 // write boundary
314 out.writeBytes(PREFIX);
315 out.writeBytes(boundary);
316 out.writeBytes(NEWLINE);
317 // write content header
318 out.writeBytes("Content-Disposition: form-data; name=\"" + name +
319 "\"; filename=\"" + fileName + "\"");
320 out.writeBytes(NEWLINE);
321 if(mimeType != null) {
322 out.writeBytes("Content-Type: " + mimeType);
323 out.writeBytes(NEWLINE);
324 }
325 out.writeBytes(NEWLINE);
326 // write content
327 out.write(data, 0, data.length);
328 out.writeBytes(NEWLINE);
329 out.flush();
330 }
331
332 /**
333 * Flushes the stream. Actually, this method does nothing, as the only
334 * write methods are highly specialized and automatically flush.
335 */
336 public void flush() {
337 // out.flush();
338 }
339
340 /**
341 * Closes the stream. <br/>
342 * <br/>
343 * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the
344 * multipart stream.
345 *
346 * @throws java.io.IOException on input/output errors
347 */
348 public void close() throws java.io.IOException {
349 // write final boundary
350 out.writeBytes(PREFIX);
351 out.writeBytes(boundary);
352 out.writeBytes(PREFIX);
353 out.writeBytes(NEWLINE);
354 out.flush();
355 out.close();
356 }
357
358 /**
359 * Gets the multipart boundary string being used by this stream.
360 *
361 * @return the boundary
362 */
363 public String getBoundary() {
364 return this.boundary;
365 }
366
367 /**
368 * Creates a new <code>java.net.URLConnection</code> object from the
369 * specified <code>java.net.URL</code>. This is a convenience method
370 * which will set the <code>doInput</code>, <code>doOutput</code>,
371 * <code>useCaches</code> and <code>defaultUseCaches</code> fields to
372 * the appropriate settings in the correct order.
373 *
374 * @return a <code>java.net.URLConnection</code> object for the URL
375 * @throws java.io.IOException on input/output errors
376 */
377 public static URLConnection createConnection(URL url)
378 throws java.io.IOException {
379 URLConnection urlConn = url.openConnection();
380 if(urlConn instanceof HttpURLConnection) {
381 HttpURLConnection httpConn = (HttpURLConnection)urlConn;
382 httpConn.setRequestMethod("POST");
383 }
384 urlConn.setDoInput(true);
385 urlConn.setDoOutput(true);
386 urlConn.setUseCaches(false);
387 urlConn.setDefaultUseCaches(false);
388 return urlConn;
389 }
390
391 /**
392 * Creates a multipart boundary string by concatenating 20 hyphens (-)
393 * and the hexadecimal (base-16) representation of the current time in
394 * milliseconds.
395 *
396 * @return a multipart boundary string
397 * @see #getContentType(String)
398 */
399 public static String createBoundary() {
400 return "--------------------" +
401 Long.toString(System.currentTimeMillis(), 16);
402 }
403
404 /**
405 * Gets the content type string suitable for the
406 * <code>java.net.URLConnection</code> which includes the multipart
407 * boundary string. <br/>
408 * <br/>
409 * This method is static because, due to the nature of the
410 * <code>java.net.URLConnection</code> class, once the output stream
411 * for the connection is acquired, it's too late to set the content
412 * type (or any other request parameter). So one has to create a
413 * multipart boundary string first before using this class, such as
414 * with the <code>createBoundary()</code> method.
415 *
416 * @param boundary the boundary string
417 * @return the content type string
418 * @see #createBoundary()
419 */
420 public static String getContentType(String boundary) {
421 return "multipart/form-data; boundary=" + boundary;
422 }
423}
Note: See TracBrowser for help on using the repository browser.