| |
| package wicket.util.upload; |
| |
| import java.io.*; |
| import java.net.*; |
| |
| /** |
| * <code>MultiPartFormOutputStream</code> is used to write |
| * "multipart/form-data" to a <code>java.net.URLConnection</code> for POSTing. |
| * This is primarily for file uploading to HTTP servers. |
| * |
| * @since JDK1.3 |
| */ |
| public class MultiPartFormOutputStream |
| { |
| /** |
| * The line end characters. |
| */ |
| private static final String NEWLINE = "\r\n"; |
| |
| /** |
| * The boundary prefix. |
| */ |
| private static final String PREFIX = "--"; |
| |
| /** |
| * The output stream to write to. |
| */ |
| private DataOutputStream out = null; |
| |
| /** |
| * The multipart boundary string. |
| */ |
| private String boundary = null; |
| |
| /** |
| * Creates a new <code>MultiPartFormOutputStream</code> object using the |
| * specified output stream and boundary. The boundary is required to be |
| * created before using this method, as described in the description for the |
| * <code>getContentType(String)</code> method. The boundary is only |
| * checked for <code>null</code> or empty string, but it is recommended to |
| * be at least 6 characters. (Or use the static createBoundary() method to |
| * create one.) |
| * |
| * @param os |
| * the output stream |
| * @param boundary |
| * the boundary |
| * @see #createBoundary() |
| * @see #getContentType(String) |
| */ |
| public MultiPartFormOutputStream(OutputStream os, String boundary) |
| { |
| if (os == null) |
| { |
| throw new IllegalArgumentException("Output stream is required."); |
| } |
| if (boundary == null || boundary.length() == 0) |
| { |
| throw new IllegalArgumentException("Boundary stream is required."); |
| } |
| this.out = new DataOutputStream(os); |
| this.boundary = boundary; |
| } |
| |
| /** |
| * Writes an boolean field value. |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, boolean value) throws java.io.IOException |
| { |
| writeField(name, new Boolean(value).toString()); |
| } |
| |
| /** |
| * Writes an double field value. |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, double value) throws java.io.IOException |
| { |
| writeField(name, Double.toString(value)); |
| } |
| |
| /** |
| * Writes an float field value. |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, float value) throws java.io.IOException |
| { |
| writeField(name, Float.toString(value)); |
| } |
| |
| /** |
| * Writes an long field value. |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, long value) throws java.io.IOException |
| { |
| writeField(name, Long.toString(value)); |
| } |
| |
| /** |
| * Writes an int field value. |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, int value) throws java.io.IOException |
| { |
| writeField(name, Integer.toString(value)); |
| } |
| |
| /** |
| * Writes an short field value. |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, short value) throws java.io.IOException |
| { |
| writeField(name, Short.toString(value)); |
| } |
| |
| /** |
| * Writes an char field value. |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, char value) throws java.io.IOException |
| { |
| writeField(name, new Character(value).toString()); |
| } |
| |
| /** |
| * Writes an string field value. If the value is null, an empty string is |
| * sent (""). |
| * |
| * @param name |
| * the field name (required) |
| * @param value |
| * the field value |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeField(String name, String value) throws java.io.IOException |
| { |
| if (name == null) |
| { |
| throw new IllegalArgumentException("Name cannot be null or empty."); |
| } |
| if (value == null) |
| { |
| value = ""; |
| } |
| /* |
| * --boundary\r\n Content-Disposition: form-data; name="<fieldName>"\r\n |
| * \r\n <value>\r\n |
| */ |
| // write boundary |
| out.writeBytes(PREFIX); |
| out.writeBytes(boundary); |
| out.writeBytes(NEWLINE); |
| // write content header |
| out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\""); |
| out.writeBytes(NEWLINE); |
| out.writeBytes(NEWLINE); |
| // write content |
| out.writeBytes(value); |
| out.writeBytes(NEWLINE); |
| out.flush(); |
| } |
| |
| /** |
| * Writes a file's contents. If the file is null, does not exists, or is a |
| * directory, a <code>java.lang.IllegalArgumentException</code> will be |
| * thrown. |
| * |
| * @param name |
| * the field name |
| * @param mimeType |
| * the file content type (optional, recommended) |
| * @param file |
| * the file (the file must exist) |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeFile(String name, String mimeType, File file) throws java.io.IOException |
| { |
| if (file == null) |
| { |
| throw new IllegalArgumentException("File cannot be null."); |
| } |
| if (!file.exists()) |
| { |
| throw new IllegalArgumentException("File does not exist."); |
| } |
| if (file.isDirectory()) |
| { |
| throw new IllegalArgumentException("File cannot be a directory."); |
| } |
| writeFile(name, mimeType, file.getCanonicalPath(), new FileInputStream(file)); |
| } |
| |
| /** |
| * Writes a input stream's contents. If the input stream is null, a |
| * <code>java.lang.IllegalArgumentException</code> will be thrown. |
| * |
| * @param name |
| * the field name |
| * @param mimeType |
| * the file content type (optional, recommended) |
| * @param fileName |
| * the file name (required) |
| * @param is |
| * the input stream |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeFile(String name, String mimeType, String fileName, InputStream is) |
| throws java.io.IOException |
| { |
| if (is == null) |
| { |
| throw new IllegalArgumentException("Input stream cannot be null."); |
| } |
| if (fileName == null || fileName.length() == 0) |
| { |
| throw new IllegalArgumentException("File name cannot be null or empty."); |
| } |
| /* |
| * --boundary\r\n Content-Disposition: form-data; name="<fieldName>"; |
| * filename="<filename>"\r\n Content-Type: <mime-type>\r\n \r\n |
| * <file-data>\r\n |
| */ |
| // write boundary |
| out.writeBytes(PREFIX); |
| out.writeBytes(boundary); |
| out.writeBytes(NEWLINE); |
| // write content header |
| out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" |
| + fileName + "\""); |
| out.writeBytes(NEWLINE); |
| if (mimeType != null) |
| { |
| out.writeBytes("Content-Type: " + mimeType); |
| out.writeBytes(NEWLINE); |
| } |
| out.writeBytes(NEWLINE); |
| // write content |
| byte[] data = new byte[1024]; |
| int r = 0; |
| while ((r = is.read(data, 0, data.length)) != -1) |
| { |
| out.write(data, 0, r); |
| } |
| // close input stream, but ignore any possible exception for it |
| try |
| { |
| is.close(); |
| } |
| catch (Exception e) |
| { |
| } |
| out.writeBytes(NEWLINE); |
| out.flush(); |
| } |
| |
| /** |
| * Writes the given bytes. The bytes are assumed to be the contents of a |
| * file, and will be sent as such. If the data is null, a |
| * <code>java.lang.IllegalArgumentException</code> will be thrown. |
| * |
| * @param name |
| * the field name |
| * @param mimeType |
| * the file content type (optional, recommended) |
| * @param fileName |
| * the file name (required) |
| * @param data |
| * the file data |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void writeFile(String name, String mimeType, String fileName, byte[] data) |
| throws java.io.IOException |
| { |
| if (data == null) |
| { |
| throw new IllegalArgumentException("Data cannot be null."); |
| } |
| if (fileName == null || fileName.length() == 0) |
| { |
| throw new IllegalArgumentException("File name cannot be null or empty."); |
| } |
| /* |
| * --boundary\r\n Content-Disposition: form-data; name="<fieldName>"; |
| * filename="<filename>"\r\n Content-Type: <mime-type>\r\n \r\n |
| * <file-data>\r\n |
| */ |
| // write boundary |
| out.writeBytes(PREFIX); |
| out.writeBytes(boundary); |
| out.writeBytes(NEWLINE); |
| // write content header |
| out.writeBytes("Content-Disposition: form-data; name=\"" + name + "\"; filename=\"" |
| + fileName + "\""); |
| out.writeBytes(NEWLINE); |
| if (mimeType != null) |
| { |
| out.writeBytes("Content-Type: " + mimeType); |
| out.writeBytes(NEWLINE); |
| } |
| out.writeBytes(NEWLINE); |
| // write content |
| out.write(data, 0, data.length); |
| out.writeBytes(NEWLINE); |
| out.flush(); |
| } |
| |
| /** |
| * Flushes the stream. Actually, this method does nothing, as the only write |
| * methods are highly specialized and automatically flush. |
| * |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void flush() throws java.io.IOException |
| { |
| // out.flush(); |
| } |
| |
| /** |
| * Closes the stream. <br /> |
| * <br /> |
| * <b>NOTE:</b> This method <b>MUST</b> be called to finalize the |
| * multipart stream. |
| * |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public void close() throws java.io.IOException |
| { |
| // write final boundary |
| out.writeBytes(PREFIX); |
| out.writeBytes(boundary); |
| out.writeBytes(PREFIX); |
| out.writeBytes(NEWLINE); |
| out.flush(); |
| out.close(); |
| } |
| |
| /** |
| * Gets the multipart boundary string being used by this stream. |
| * |
| * @return the boundary |
| */ |
| public String getBoundary() |
| { |
| return this.boundary; |
| } |
| |
| /** |
| * Creates a new <code>java.net.URLConnection</code> object from the |
| * specified <code>java.net.URL</code>. This is a convenience method |
| * which will set the <code>doInput</code>, <code>doOutput</code>, |
| * <code>useCaches</code> and <code>defaultUseCaches</code> fields to |
| * the appropriate settings in the correct order. |
| * |
| * @return a <code>java.net.URLConnection</code> object for the URL |
| * @throws java.io.IOException |
| * on input/output errors |
| */ |
| public static URLConnection createConnection(URL url) throws java.io.IOException |
| { |
| URLConnection urlConn = url.openConnection(); |
| if (urlConn instanceof HttpURLConnection) |
| { |
| HttpURLConnection httpConn = (HttpURLConnection)urlConn; |
| httpConn.setRequestMethod("POST"); |
| } |
| urlConn.setDoInput(true); |
| urlConn.setDoOutput(true); |
| urlConn.setUseCaches(false); |
| urlConn.setDefaultUseCaches(false); |
| return urlConn; |
| } |
| |
| /** |
| * Creates a multipart boundary string by concatenating 20 hyphens (-) and |
| * the hexadecimal (base-16) representation of the current time in |
| * milliseconds. |
| * |
| * @return a multipart boundary string |
| * @see #getContentType(String) |
| */ |
| public static String createBoundary() |
| { |
| return "--------------------" + Long.toString(System.currentTimeMillis(), 16); |
| } |
| |
| /** |
| * Gets the content type string suitable for the |
| * <code>java.net.URLConnection</code> which includes the multipart |
| * boundary string. <br /> |
| * <br /> |
| * This method is static because, due to the nature of the |
| * <code>java.net.URLConnection</code> class, once the output stream for |
| * the connection is acquired, it's too late to set the content type (or any |
| * other request parameter). So one has to create a multipart boundary |
| * string first before using this class, such as with the |
| * <code>createBoundary()</code> method. |
| * |
| * @param boundary |
| * the boundary string |
| * @return the content type string |
| * @see #createBoundary() |
| */ |
| public static String getContentType(String boundary) |
| { |
| return "multipart/form-data; boundary=" + boundary; |
| } |
| } |