Http Multipart enables POSTing of several files in one go. I didn’t found any good supported way of doing this from Android so I created some helpers for achieving this.
The Multipart is quite strange (ugly) protocol. See reference for more details.
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
public class MultiParts { private final static String CRLF = "\r\n"; private final static String DASHES = "--"; private final static String CONTENT_TYPE = "Content-Type: "; private final static String FORM_DATA = "Content-Disposition: form-data; "; private final static String NAME = "name="; private final static String FILENAME = "; filename="; private final static String QUOTE = "\""; public static final String FILES_START = "files["; public static final String FILES_END = "]"; private final List<MultiParts.Part> parts = new ArrayList<MultiParts.Part>(); private final String boundary = UUID.randomUUID().toString(); public byte[] encode() throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(out); dout.writeBytes(DASHES); dout.writeBytes(getBoundary()); dout.writeBytes(CRLF); Iterator<MultiParts.Part> it = parts.iterator(); while (it.hasNext()) { MultiParts.Part part = it.next(); part.writeTo(dout); if (it.hasNext()) { dout.writeBytes(CRLF); } } dout.writeBytes(DASHES); dout.writeBytes(CRLF); return out.toByteArray(); } public static MultiParts.PartBuilder newMultiParts() { return new MultiParts.PartBuilder(); } public String getBoundary() { return boundary; } // ---------------------------------------- public static final class PartBuilder { private MultiParts multi = new MultiParts(); public MultiParts.PartBuilder addBinary(String key, byte [] data, String type) throws IOException { multi.parts.add(new MultiParts.FilePart(multi.getBoundary(), key, data, type)); return this; } public MultiParts.PartBuilder addFile(String key, File file, String type) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); FileInputStream ins = new FileInputStream(file); pipe(ins, out); addBinary(key, out.toByteArray(), type); return this; } public MultiParts.PartBuilder add(String key, String str) throws UnsupportedEncodingException { multi.parts.add(new MultiParts.StringPart(multi.getBoundary(), key, str.getBytes("UTF-8"), "text/plain; charset=utf-8")); return this; } public MultiParts.PartBuilder addHtml(String key, String str) throws UnsupportedEncodingException { multi.parts.add(new MultiParts.StringPart(multi.getBoundary(), key, str.getBytes("UTF-8"), "text/html; charset=utf-8")); return this; } public MultiParts build() { return multi; } private void pipe(InputStream is, OutputStream os) throws IOException { int n; byte[] buffer = new byte[1024]; while ((n = is.read(buffer)) > -1) { os.write(buffer, 0, n); } is.close(); os.close(); } } public static final class FilePart extends MultiParts.Part { private FilePart(String boundary, String name, byte[] data, String type) { super(boundary, name, data, type); } @Override protected void writeContentType(DataOutputStream dout) throws IOException { dout.writeBytes(type); } @Override protected void writeContentDisposition(DataOutputStream dout) throws IOException { dout.writeBytes(NAME); dout.writeBytes(QUOTE); dout.writeBytes(FILES_START); dout.writeBytes(name); dout.writeBytes(FILES_END); dout.writeBytes(QUOTE); dout.writeBytes(FILENAME); dout.writeBytes(QUOTE); dout.writeBytes(name); dout.writeBytes(QUOTE); } } public static final class StringPart extends MultiParts.Part { private StringPart(String boundary, String name, byte[] data, String type) { super(boundary, name, data, type); } @Override protected void writeContentDisposition(DataOutputStream dout) throws IOException { dout.writeBytes(NAME); dout.writeBytes(QUOTE); dout.writeBytes(name); dout.writeBytes(QUOTE); } } public static abstract class Part { protected String name; protected byte[] data; protected String type; protected String boundary; private Part(String boundary, String name, byte[] data, String type) { super(); this.boundary = boundary; this.name = name; this.data = data; this.type = type; } protected void writeContentType(DataOutputStream dout) throws IOException { dout.writeBytes(type); } protected void writeContentDisposition(DataOutputStream dout) throws IOException { } public String getName() { return name; } public byte[] getData() { return data; } public String getType() { return type; } public void writeTo(DataOutputStream dout) throws IOException { dout.writeBytes(FORM_DATA); writeContentDisposition(dout); dout.writeBytes(CRLF); dout.writeBytes(CONTENT_TYPE); writeContentType(dout); dout.writeBytes(CRLF); dout.writeBytes(CRLF); dout.write(data); dout.writeBytes(CRLF); dout.writeBytes(DASHES); dout.writeBytes(boundary); } } } |
An Enum that keeps supported formats. Add which ever you need.
The Enum contains the Http Mime type and the file extension.
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 33 34 35 36 |
public enum MimeType { PNG("image/png", ".png"), JPG("image/jpg", ".jpg"), JPEG("image/jpeg", ".jpeg"), GIF("image/gif", ".gif"), BINARY("application/octet-stream", "*"); private final String extension; private final String type; private MimeType(String type, String extension) { this.type = type; this.extension = extension; } public String getExtension() { return extension; } public String getType() { return type; } public static MimeType fromName(String name) { MimeType result = MimeType.BINARY; String str = name.toLowerCase(); for(MimeType mime : values()) { if(str.endsWith(mime.getExtension())) { result = mime; break; } } return result; } } |
The HttpUtil.java wraps things up and integrate with Internet 😉
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
public class HttpUtil { public static final String USER_AGENT = "Mozilla/5.0"; public static String post(String url, MultiParts parts) throws IOException { HttpURLConnection conn = prepareConnection(url); conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + parts.getBoundary()); return postData(conn, parts.encode()); } public static String post(String url, String content, String encoding) throws IOException { HttpURLConnection conn = prepareConnection(url); return postData(conn, content.getBytes(encoding)); } public static String post(String url, byte[] data) throws IOException { HttpURLConnection conn = prepareConnection(url); return postData(conn, data); } private static String postData(HttpURLConnection con, byte[] bytes) throws IOException { writeBytes(con, bytes); int responseCode = con.getResponseCode(); if (responseCode != 200) { throw new IOException("Response code: " + responseCode); } String result = readString(con); return result; } private static HttpURLConnection prepareConnection(String url) throws IOException { HttpURLConnection conn; String lowerUrl = url.toLowerCase(); if (lowerUrl.startsWith("https")) { conn = prepareHttps(url); } else if (lowerUrl.startsWith("http")) { conn = prepareHttp(url); } else { throw new IllegalArgumentException("Only protocol HTTP or HTTPS is allowed"); } return conn; } private static HttpsURLConnection prepareHttps(String url) throws IOException { URL obj = new URL(url); HttpsURLConnection con = (HttpsURLConnection) obj.openConnection(); preparePost(con); return con; } private static HttpURLConnection prepareHttp(String url) throws IOException { URL obj = new URL(url); HttpURLConnection con = (HttpURLConnection) obj.openConnection(); preparePost(con); return con; } private static void writeBytes(HttpURLConnection con, byte[] bytes) throws IOException { DataOutputStream wr = null; try { wr = new DataOutputStream(con.getOutputStream()); wr.write(bytes); wr.flush(); } finally { if (wr != null) { wr.close(); } } } private static String readString(HttpURLConnection con) throws IOException { StringBuffer response = new StringBuffer(); BufferedReader in = null; try { in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; while ((inputLine = in.readLine()) != null) { response.append(inputLine); } } finally { if (in != null) { in.close(); } } return response.toString(); } private static void preparePost(HttpURLConnection con) throws ProtocolException { con.setRequestMethod("POST"); con.setRequestProperty("User-Agent", USER_AGENT); con.setRequestProperty("Accept-Language", "en-US,en;q=0.5"); con.setDoOutput(true); } } |