AndroidでHTTPのアップロードを使う
イメージ的には、
// ファイルを追加 MultipartEntity me = new MultipartEntity(); for (File file : files) me.AddFileEntry(new FileInputStream(file), file.getName()); // POSTリクエストの作成 HttpPost post = new HttpPost("http://server_to_upload_onto.example.com"); post.setEntity(me); // 投げる DefaultHttpClient client = new DefaultHttpClient(); HttpResponse res = client.execute(post); int status = res.getStatusLine().getStatusCode(); ...
ってやりたいんですが、ファイルのエントリを足す毎にメモリを消費されると、ファイルの個数が多くなったときにヤバいことになるので、なるべくメモリを使いたくない。でも、標準ではそんなHttpEntityはないし、外部ライブラリにも頼りたくない。
っていう感じで、さくっと書いてみました。
class MultipartEntity implements HttpEntity { String _boundary = UUID.randomUUID().toString(); String _contentType = String.format("multipart/form-data; boundary=%s", _boundary); class FileEntry { public InputStream stream; public String fileName; } ArrayList<FileEntry> _entries = new ArrayList<FileEntry>(); MultipartEntity() { } void AddFileEntry(InputStream stream, String fileName) { FileEntry fe = new FileEntry(); fe.stream = stream; fe.fileName = fileName; _entries.add(fe); } void AddFileEntry(File file) throws FileNotFoundException { AddFileEntry(new FileInputStream(file), file.getName()); } @Override public void consumeContent() throws IOException { } @Override public InputStream getContent() throws IOException, IllegalStateException { return new MultipartInputStream(); } @Override public Header getContentEncoding() { return null; } @Override public long getContentLength() { return -1; } @Override public Header getContentType() { return new BasicHeader("Content-Type", _contentType); } @Override public boolean isChunked() { return false; } @Override public boolean isRepeatable() { return false; } @Override public boolean isStreaming() { return false; } @Override public void writeTo(OutputStream outstream) throws IOException { byte[] buf = new byte[1024 * 1024]; InputStream is = getContent(); for (; ;) { int ret = is.read(buf); if (ret < 0) break; outstream.write(buf); } } class MultipartInputStream extends InputStream { ArrayList<InputStream> _streams = new ArrayList<InputStream>(); int _pos = 0; int _markPos = 0; MultipartInputStream() { int count = _entries.size(); String hdr = ""; for (int i = 0; i < count; i++) { hdr += String.format( "--%s\r\n" + "Content-Disposition: form-data; name=\"file-%d\"; filename=\"%s\"\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Transfer-Encoding: binary\r\n\r\n", _boundary, i, _entries.get(i).fileName); _streams.add(new ByteArrayInputStream(hdr.getBytes())); _streams.add(_entries.get(i).stream); hdr = String.format("\r\n--%s%s\r\n", _boundary, i + 1 == count ? "--" : ""); } _streams.add(new ByteArrayInputStream(hdr.getBytes())); } @Override public int read() throws IOException { for (; _pos < _streams.size(); _pos++) { int ret = _streams.get(_pos).read(); if (ret >= 0) return ret; } return -1; } @Override public int available() throws IOException { for (; _pos < _streams.size(); _pos++) { int ret = _streams.get(_pos).available(); if (ret > 0) return ret; } return 0; } @Override public void close() throws IOException { for (int i = 0; i < _streams.size(); i++) { _streams.get(i).close(); } } @Override public void mark(int readlimit) { _markPos = _pos; _streams.get(_pos).mark(readlimit); } @Override public boolean markSupported() { for (int i = 0; i < _streams.size(); i++) { if (!_streams.get(_pos).markSupported()) return false; } return true; } @Override public int read(byte[] buffer, int offset, int length) throws IOException { int all = 0; int ret = -1; for (; _pos < _streams.size(); _pos++) { for (;;) { ret = _streams.get(_pos).read(buffer, offset, length); Log.i("MultipartInputStream", String.format("Stream #%d: %d bytes (of %d bytes) to offset %d", _pos, ret, length, offset)); if (ret <= 0) break; all += ret; offset += ret; length -= ret; if (length == 0) { Log.i("MultipartInputStream", String.format("Read: %d bytes", all)); return all; } } } Log.i("MultipartInputStream", String.format("Read: %d bytes (ret=%d)", all, ret)); return ret < 0 && all == 0 ? -1 : all; } @Override public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override public synchronized void reset() throws IOException { _pos = _markPos; _streams.get(_pos).reset(); } @Override public long skip(long byteCount) throws IOException { long allSkipped = 0; for (; _pos < _streams.size(); _pos++) { long skipped = _streams.get(_pos).skip(byteCount); allSkipped += skipped; byteCount -= skipped; if (byteCount == 0) break; } return allSkipped; } } }