over 4 years ago

HTTPでファイル送信をクライアントからする場合、幾つかやり方はありますが、一般的にはHTTPマルチパートデータ形式でファイル送信をおこなうのではと思います。

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
 
import android.util.Log;
 
public class HTTPMultiPart {
 
    public HTTPMultiPart() {
    }
 
    private void sendData(String api, String filename, String id, String password) {
 
        HttpURLConnection connection = null;
        DataOutputStream outputStream = null;
 
        String lineEnd = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
 
        int bytesRead, bytesAvailable, bufferSize;
        byte[] buffer;
        int maxBufferSize = 1 * 1024 * 1024;
 
        try {
            // サーバへの接続開始
            URL url = new URL(api);
            connection = (HttpURLConnection) url.openConnection();
 
            connection.setDoInput(true);
            connection.setDoOutput(true);

            // キャッシュを使用しない
            connection.setUseCaches(false);
 
            connection.setRequestProperty("User-Agent", "HTTPMultiPart/1.0.0 (Linux; Android;)");
            connection.setRequestMethod("POST");
            // HTTP/1.1 でサポートされた持続接続機能が使用できることを宣言する
            connection.setRequestProperty("Connection", "Keep-Alive");
 
            // マルチパートデータとして複数のデータを送ることを宣言する
            connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

            StringBuilder builder = new StringBuilder();
            builder.append(twoHyphens + boundary + lineEnd);
            builder.append("Content-Disposition: form-data; name=\"id\"" + lineEnd);
            builder.append(lineEnd);
            builder.append(id);
            builder.append(lineEnd);
            builder.append(twoHyphens + boundary + lineEnd);
            builder.append("Content-Disposition: form-data; name=\"password\"" + lineEnd);
            builder.append(lineEnd);
            builder.append(password);
            builder.append(lineEnd);
            builder.append(twoHyphens + boundary + lineEnd);
            builder.append("Content-Disposition: form-data; name=\"uploadedfile\";filename=\"" + filename + "\"" + lineEnd);
            builder.append(lineEnd);
            File file = new File(filename);
            FileInputStream fileInputStream = new FileInputStream(file);
            StringBuilder lastbuilder = new StringBuilder();
            lastbuilder.append(lineEnd + twoHyphens + boundary + twoHyphens + lineEnd);
 
            // 内部バッファ処理を行わずに HTTP 要求本体のストリーミングを有効にする
            connection.setChunkedStreamingMode(0);
 
            // setFixedLengthStreamingMode をつかう場合
            // Content-length を算出する
            // int contentLength = (int) (builder.toString().getBytes().length
            // + lastbuilder.toString().getBytes().length
            // + file.length());
            // connection.setFixedLengthStreamingMode(contentLength);
 
            outputStream = new DataOutputStream(connection.getOutputStream());
            outputStream.writeBytes(builder.toString());
 
            // ファイルを一定サイズ毎読み書きする
            bytesAvailable = fileInputStream.available();
            bufferSize = Math.min(bytesAvailable, maxBufferSize);
            buffer = new byte[bufferSize];
            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(lastbuilder.toString());
 
            String result = "";
 
            // 成功ならレスポンスボディをそのまま表示する
            if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
                String line = null;
                while ((line = br.readLine()) != null) {
                    result += line + "\n";
                }
            } else {
                result = "Server error is occured";
            }
 
            try {
                fileInputStream.close();
                outputStream.flush();
                outputStream.close();
                connection.disconnect();
            } catch (Exception e) {
                // Android 2.2 以下だと flush() あたりで java.io.IOException: Stream is
                // closed が発生するが、ファイルは無事に届いているはず。
                // Default buffer size used in BufferedReader constructor. It
                // would be better to be explicit if an 8k-char buffer is
                // required. が関係しているような気がする。
                e.printStackTrace();
            }
            Log.v("HTTPMultiPart", result);
        } catch (FileNotFoundException e) {
            Log.v("HTTPMultiPart", "File Not Found.");
        } catch (Exception ex) {
            Log.v("HTTPMultiPart", "Network error is occured. Please confirm your network connection.");
        }
    }
}

ファイルを一定サイズ毎読み書きする部分は、進捗状況の更新に利用できるでしょう。また、少し修正を加えれば複数のファイル送信もできます。ファイルの読み込みは一定量に限って行われますので、Out of Memory はほぼ発生しないはずです。

なお、OSSライブラリでHTTPマルチパートデータ送信を実現できるものが幾つかありますので、特別な理由がなければそれらを利用するほうがいいでしょう。


参照

http://www.anddev.org/novice-tutorials-f8/upload-files-to-web-server-t443-90.html
http://www.kanzaki.com/docs/html/htminfo32.html

← Antで自動的に環境切り替えをしてビルドする jsonschema2pojo - JsonフォーマットのデータからJavaクラスを自動生成する →