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

Last change on this file since 2667 was 2641, checked in by Gubaer, 14 years ago

new: supports system defined proxies if JOSM is started with -Djava.net.useSystemProxies=true
fixed #1641: JOSM doesn't allow for setting HTTP proxy user/password distrinct from OSM server user/password
fixed #2865: SOCKS Proxy Support
fixed #4182: Proxy Authentication

  • Property svn:eol-style set to native
File size: 14.6 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 }
95
96 /**
97 * Writes an boolean field value.
98 *
99 * @param name the field name (required)
100 * @param value the field value
101 * @throws java.io.IOException on input/output errors
102 */
103 public void writeField(String name, boolean value)
104 throws java.io.IOException {
105 writeField(name, Boolean.valueOf(value).toString());
106 }
107
108 /**
109 * Writes an double field value.
110 *
111 * @param name the field name (required)
112 * @param value the field value
113 * @throws java.io.IOException on input/output errors
114 */
115 public void writeField(String name, double value)
116 throws java.io.IOException {
117 writeField(name, Double.toString(value));
118 }
119
120 /**
121 * Writes an float field value.
122 *
123 * @param name the field name (required)
124 * @param value the field value
125 * @throws java.io.IOException on input/output errors
126 */
127 public void writeField(String name, float value)
128 throws java.io.IOException {
129 writeField(name, Float.toString(value));
130 }
131
132 /**
133 * Writes an long field value.
134 *
135 * @param name the field name (required)
136 * @param value the field value
137 * @throws java.io.IOException on input/output errors
138 */
139 public void writeField(String name, long value)
140 throws java.io.IOException {
141 writeField(name, Long.toString(value));
142 }
143
144 /**
145 * Writes an int field value.
146 *
147 * @param name the field name (required)
148 * @param value the field value
149 * @throws java.io.IOException on input/output errors
150 */
151 public void writeField(String name, int value)
152 throws java.io.IOException {
153 writeField(name, Integer.toString(value));
154 }
155
156 /**
157 * Writes an short field value.
158 *
159 * @param name the field name (required)
160 * @param value the field value
161 * @throws java.io.IOException on input/output errors
162 */
163 public void writeField(String name, short value)
164 throws java.io.IOException {
165 writeField(name, Short.toString(value));
166 }
167
168 /**
169 * Writes an char field value.
170 *
171 * @param name the field name (required)
172 * @param value the field value
173 * @throws java.io.IOException on input/output errors
174 */
175 public void writeField(String name, char value)
176 throws java.io.IOException {
177 writeField(name, Character.valueOf(value).toString());
178 }
179
180 /**
181 * Writes an string field value. If the value is null, an empty string
182 * is sent ("").
183 *
184 * @param name the field name (required)
185 * @param value the field value
186 * @throws java.io.IOException on input/output errors
187 */
188 public void writeField(String name, String value)
189 throws java.io.IOException {
190 if(name == null)
191 throw new IllegalArgumentException("Name cannot be null or empty.");
192 if(value == null) {
193 value = "";
194 }
195 /*
196 --boundary\r\n
197 Content-Disposition: form-data; name="<fieldName>"\r\n
198 \r\n
199 <value>\r\n
200 */
201 // write boundary
202 out.writeBytes(PREFIX);
203 out.writeBytes(boundary);
204 out.writeBytes(NEWLINE);
205 // write content header
206 out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"");
207 out.writeBytes(NEWLINE);
208 out.writeBytes(NEWLINE);
209 // write content
210 out.writeBytes(value);
211 out.writeBytes(NEWLINE);
212 out.flush();
213 }
214
215 /**
216 * Writes a file's contents. If the file is null, does not exists, or
217 * is a directory, a <code>java.lang.IllegalArgumentException</code>
218 * will be thrown.
219 *
220 * @param name the field name
221 * @param mimeType the file content type (optional, recommended)
222 * @param file the file (the file must exist)
223 * @throws java.io.IOException on input/output errors
224 */
225 public void writeFile(String name, String mimeType, java.io.File file)
226 throws java.io.IOException {
227 if(file == null)
228 throw new IllegalArgumentException("File cannot be null.");
229 if(!file.exists())
230 throw new IllegalArgumentException("File does not exist.");
231 if(file.isDirectory())
232 throw new IllegalArgumentException("File cannot be a directory.");
233 writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(file));
234 }
235
236 /**
237 * Writes a input stream's contents. If the input stream is null, a
238 * <code>java.lang.IllegalArgumentException</code> will be thrown.
239 *
240 * @param name the field name
241 * @param mimeType the file content type (optional, recommended)
242 * @param fileName the file name (required)
243 * @param is the input stream
244 * @throws java.io.IOException on input/output errors
245 */
246 public void writeFile(String name, String mimeType,
247 String fileName, InputStream is)
248 throws java.io.IOException {
249 if(is == null)
250 throw new IllegalArgumentException("Input stream cannot be null.");
251 if(fileName == null || fileName.length() == 0)
252 throw new IllegalArgumentException("File name cannot be null or empty.");
253 /*
254 --boundary\r\n
255 Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
256 Content-Type: <mime-type>\r\n
257 \r\n
258 <file-data>\r\n
259 */
260 // write boundary
261 out.writeBytes(PREFIX);
262 out.writeBytes(boundary);
263 out.writeBytes(NEWLINE);
264 // write content header
265 out.writeBytes("Content-Disposition: form-data; name=\"" + name +
266 "\"; filename=\"" + fileName + "\"");
267 out.writeBytes(NEWLINE);
268 if(mimeType != null) {
269 out.writeBytes("Content-Type: " + mimeType);
270 out.writeBytes(NEWLINE);
271 }
272 out.writeBytes(NEWLINE);
273 // write content
274 byte[] data = new byte[1024];
275 int r = 0;
276 while((r = is.read(data, 0, data.length)) != -1) {
277 out.write(data, 0, r);
278 }
279 // close input stream, but ignore any possible exception for it
280 try {
281 is.close();
282 } catch(Exception e) {}
283 out.writeBytes(NEWLINE);
284 out.flush();
285 }
286
287 /**
288 * Writes the given bytes. The bytes are assumed to be the contents
289 * of a file, and will be sent as such. If the data is null, a
290 * <code>java.lang.IllegalArgumentException</code> will be thrown.
291 *
292 * @param name the field name
293 * @param mimeType the file content type (optional, recommended)
294 * @param fileName the file name (required)
295 * @param data the file data
296 * @throws java.io.IOException on input/output errors
297 */
298 public void writeFile(String name, String mimeType,
299 String fileName, byte[] data)
300 throws java.io.IOException {
301 if(data == null)
302 throw new IllegalArgumentException("Data cannot be null.");
303 if(fileName == null || fileName.length() == 0)
304 throw new IllegalArgumentException("File name cannot be null or empty.");
305 /*
306 --boundary\r\n
307 Content-Disposition: form-data; name="<fieldName>"; filename="<filename>"\r\n
308 Content-Type: <mime-type>\r\n
309 \r\n
310 <file-data>\r\n
311 */
312 // write boundary
313 out.writeBytes(PREFIX);
314 out.writeBytes(boundary);
315 out.writeBytes(NEWLINE);
316 // write content header
317 out.writeBytes("Content-Disposition: form-data; name=\"" + name +
318 "\"; filename=\"" + fileName + "\"");
319 out.writeBytes(NEWLINE);
320 if(mimeType != null) {
321 out.writeBytes("Content-Type: " + mimeType);
322 out.writeBytes(NEWLINE);
323 }
324 out.writeBytes(NEWLINE);
325 // write content
326 out.write(data, 0, data.length);
327 out.writeBytes(NEWLINE);
328 out.flush();
329 }
330
331 /**
332 * Flushes the stream. Actually, this method does nothing, as the only
333 * write methods are highly specialized and automatically flush.
334 */
335 public void flush() {
336 // out.flush();
337 }
338
339 /**
340 * Closes the stream. <br/>
341 * <br/>
342 * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the
343 * multipart stream.
344 *
345 * @throws java.io.IOException on input/output errors
346 */
347 public void close() throws java.io.IOException {
348 // write final boundary
349 out.writeBytes(PREFIX);
350 out.writeBytes(boundary);
351 out.writeBytes(PREFIX);
352 out.writeBytes(NEWLINE);
353 out.flush();
354 out.close();
355 }
356
357 /**
358 * Gets the multipart boundary string being used by this stream.
359 *
360 * @return the boundary
361 */
362 public String getBoundary() {
363 return this.boundary;
364 }
365
366 /**
367 * Creates a new <code>java.net.URLConnection</code> object from the
368 * specified <code>java.net.URL</code>. This is a convenience method
369 * which will set the <code>doInput</code>, <code>doOutput</code>,
370 * <code>useCaches</code> and <code>defaultUseCaches</code> fields to
371 * the appropriate settings in the correct order.
372 *
373 * @return a <code>java.net.URLConnection</code> object for the URL
374 * @throws java.io.IOException on input/output errors
375 */
376 public static URLConnection createConnection(URL url)
377 throws java.io.IOException {
378 URLConnection urlConn = url.openConnection();
379 if(urlConn instanceof HttpURLConnection) {
380 HttpURLConnection httpConn = (HttpURLConnection)urlConn;
381 httpConn.setRequestMethod("POST");
382 }
383 urlConn.setDoInput(true);
384 urlConn.setDoOutput(true);
385 urlConn.setUseCaches(false);
386 urlConn.setDefaultUseCaches(false);
387 return urlConn;
388 }
389
390 /**
391 * Creates a multipart boundary string by concatenating 20 hyphens (-)
392 * and the hexadecimal (base-16) representation of the current time in
393 * milliseconds.
394 *
395 * @return a multipart boundary string
396 * @see #getContentType(String)
397 */
398 public static String createBoundary() {
399 return "--------------------" +
400 Long.toString(System.currentTimeMillis(), 16);
401 }
402
403 /**
404 * Gets the content type string suitable for the
405 * <code>java.net.URLConnection</code> which includes the multipart
406 * boundary string. <br/>
407 * <br/>
408 * This method is static because, due to the nature of the
409 * <code>java.net.URLConnection</code> class, once the output stream
410 * for the connection is acquired, it's too late to set the content
411 * type (or any other request parameter). So one has to create a
412 * multipart boundary string first before using this class, such as
413 * with the <code>createBoundary()</code> method.
414 *
415 * @param boundary the boundary string
416 * @return the content type string
417 * @see #createBoundary()
418 */
419 public static String getContentType(String boundary) {
420 return "multipart/form-data; boundary=" + boundary;
421 }
422}
Note: See TracBrowser for help on using the repository browser.