AndroidからHTTPのmultipart/form-dataを使ってデータをアップロードする


AndroidのHTTP Clientとしては`org.apache.http`にあるライブラリ群と`java.net.HttpURLConnection`があるが，`org.apache.http`は推奨されていない．さらに`multipart/form-data`を使ってファイルをアップロードするにはクラスがいくつか足らない．そこで，`java.net.HttpURLConnection`を使ってファイルをアップロードする簡単なライブラリを書いてみた．コードは[Android snippets](http://www.androidsnippets.com/multipart-http-requests)を参考に実装した．私の改変部分はMITライセンスでどうぞ．もとのAndroid SnippetsのコードはPublic Domainです．

```java
package info.informationsea.apps;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.util.Map;
import java.util.UUID;

/**
 * Copyright (C) 2014 Yasunobu OKAMURA
 * MIT License
 */
public class HttpMultipartSender {
    private HttpMultipartSender(){} // cannot make instance

    // this function is implemented based on http://www.androidsnippets.com/multipart-http-requests
    public static void sendMultipart(HttpURLConnection connection, String filefield, File filepath, Map<String, String> textdata) throws IOException {

        final String twoHyphens = "--";
        final String boundary =  "*****"+ UUID.randomUUID().toString()+"*****";
        final String lineEnd = "\r\n";
        final int maxBufferSize = 1024*1024*3;

        DataOutputStream outputStream;

        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setUseCaches(false);

        connection.setRequestMethod("POST");
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Content-Type", "multipart/form-data; boundary="+boundary);

        outputStream = new DataOutputStream(connection.getOutputStream());
        outputStream.writeBytes(twoHyphens + boundary + lineEnd);
        outputStream.writeBytes("Content-Disposition: form-data; name=\"" + filefield + "\"; filename=\"" + filepath.getName() +"\"" + lineEnd);
        outputStream.writeBytes("Content-Type: application/octet-stream" + lineEnd);
        outputStream.writeBytes("Content-Transfer-Encoding: binary" + lineEnd);
        outputStream.writeBytes(lineEnd);

        FileInputStream fileInputStream = new FileInputStream(filepath);
        int bytesAvailable = fileInputStream.available();
        int bufferSize = Math.min(bytesAvailable, maxBufferSize);
        byte[] buffer = new byte[bufferSize];

        int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
        while(bytesRead > 0) {
            outputStream.write(buffer, 0, bufferSize);
            bytesAvailable = fileInputStream.available();
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            bytesRead = fileInputStream.read(buffer, 0, bufferSize);
        }

        outputStream.writeBytes(lineEnd);

        for (Map.Entry<String, String> entry : textdata.entrySet()) {
            outputStream.writeBytes(twoHyphens + boundary + lineEnd);
            outputStream.writeBytes("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + lineEnd);
            outputStream.writeBytes("Content-Type: text/plain"+lineEnd);
            outputStream.writeBytes(lineEnd);
            outputStream.writeBytes(entry.getValue());
            outputStream.writeBytes(lineEnd);
        }

        outputStream.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);

        outputStream.close();
    }
}
```