主要类:
/** * <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 */ @SuppressLint("UseValueOf") 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 */ @SuppressLint("UseValueOf") 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(new String(value.getBytes("UTF-8"), "ISO-8859-1")); 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=\"" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1") + "\""); 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=\"" + new String(fileName.getBytes("UTF-8"), "ISO-8859-1") + "\""); 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; } }
测试代码:
public static void main(String[] args) throws Exception { String boundary = MultiPartFormOutputStream.createBoundary(); InputStream is = null; ByteArrayOutputStream baos = null; try { String url = "http://localhost:8080/test"; HttpURLConnection conn = (HttpURLConnection)MultiPartFormOutputStream.createConnection(new URL(url)); conn.addRequestProperty("Content-Type", MultiPartFormOutputStream.getContentType(boundary)); MultiPartFormOutputStream s = new MultiPartFormOutputStream(conn.getOutputStream(), boundary); s.writeField("id", 3L); s.writeField("content", "测试内容22abc"); s.writeFile("file", null, new File("/Users/test/image.png")); s.close(); int responseCode = conn.getResponseCode(); if (responseCode != 200) { throw new Exception(String.format("Received the response code %d from the URL %s", responseCode, url)); } // Read the response is = conn.getInputStream(); baos = new ByteArrayOutputStream(); byte[] bytes = new byte[1024]; int bytesRead; while((bytesRead = is.read(bytes)) != -1) { baos.write(bytes, 0, bytesRead); } byte[] bytesReceived = baos.toByteArray(); String response = new String(bytesReceived); System.out.println(response); } catch (IOException e) { e.printStackTrace(); } finally { baos.close(); is.close(); }