LoginSignup
6
6

More than 5 years have passed since last update.

Android Volley の Request にマルチパートの処理を追加する

Last updated at Posted at 2015-07-21

Multipart

HTML などで、ファイルを送る時に行われる処理をご存知であれば、理解しやすいかも。。。

<form action="URL" method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
</form>

このような処理を書いたこと一度はあるともいます。
enctype を指定することで、ファイルデーターをサーバー側に送信できるようになります。

Volley は?

マルチパートを Volley にも標準で対応してほしいものですが、対応されていないようです。

OkHttpという便利なライブラリがあるが、今回は Apatch HttpClient MimeHttpComponents Client For Android を利用し対応します。

ライブラリを Gradle に設定

※ライブラリのバージョンによって挙動や実際の実装内容が異なります。今回のバージョンと違うバージョンを利用される時は注意ください。

dependencies {
...
    compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
    compile 'org.apache.httpcomponents:httpclient-cache:4.3.6'
    compile 'org.apache.httpcomponents:httpclient:4.3.6'
    compile 'org.apache.httpcomponents:httpmime:4.3.6'
    compile 'org.apache.httpcomponents:fluent-hc:4.3.6'
...
}

Android Studio の [Sync Project With Gradle File] ボタンを押すと、記述したライブラリのバージョンをインストールしてくれます。
これで、ライブラリを使う準備が整いました。

MultipartRequest を作る

まず、マルチパートの処理は、リクエストの処理の一環なので、Volley の Request<T> を拡張したいと思います。

/**
 * Created by shun_nakahara on 7/9/15.
 *
 * Base class for all network multipart requests.
 * ContentType MULTIPART_FORM_DATA 形式のファイルを送る為のクラスです。
 *
 * @author shun_nakahara
 *
 * @param <T> The type of parsed response this request expects.
 */
public abstract class MultipartRequest<T> extends Request<T> {
...
}

MultipartEntityBuilder

マルチパートのパラメータビルド処理を行うクラス

/**
 * Multipart のパラメータビルド処理を担うクラス
 */
private final MultipartEntityBuilder mMultipartEntityBuilder = MultipartEntityBuilder.create();

こちらのパラメータは必ず利用するので、クラス作成されたタイミングで、.create() しておきます。

HttpEntity

コンテンツ情報など格納

/**
 * コンテンツ設定に必要なデーターを格納
 */
public HttpEntity mHttpEntity;

MultipartEntityBuilder から作成される値を保持する。

MultipartRequest (コンストラクタ)

送りたい Request Params を MultipartEntityBuilder に追加していく。
今回は、テキストと画像のバイナリデーターを追加します。

/**
 * {@link MultipartRequest} Constructor
 *
 * @param url {@link String}
 * @param requestStringBody {@link LinkedHashMap}
 * @param requestFileBody {@link LinkedHashMap}
 * @param requestFileName {@link LinkedHashMap}
 * @param listener {@link Response.Listener}
 * @param errorListener {@link Response.ErrorListener}
 */
public MultipartRequest(@NonNull String url, @Nullable LinkedHashMap<String, String> requestStringBody, @Nullable LinkedHashMap<String, byte[]> requestFileBody, @Nullable LinkedHashMap<String, String> requestFileName, @NonNull Response.Listener<T> listener, @NonNull Response.ErrorListener errorListener) {
        super(Method.POST, url, errorListener);
        mistener = listener;

        this.mMultipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        this.mMultipartEntityBuilder.setCharset(Charset.defaultCharset());

        if (requestStringBody != null) {
            ContentType contentType = ContentType.create("text/plain", Consts.UTF_8);
            for (LinkedHashMap.Entry<String, String> entry : requestStringBody.entrySet()) {
                this.mMultipartEntityBuilder.addTextBody(entry.getKey(), entry.getValue(), contentType);
            }
        }

        if (requestFileBody != null && requestFileName != null) {
            for (LinkedHashMap.Entry<String, byte[]> entry : requestFileBody.entrySet()) {
                this.mMultipartEntityBuilder.addBinaryBody(entry.getKey(), entry.getValue(), ContentType.MULTIPART_FORM_DATA, requestFileName.get(entry.getKey()));
            }
        }

    }

getBodyContentType

POST PUT の時の Body ContentType を MultipartEntityBuilder から取得する

/**
 * Returns the content type of the POST or PUT body.<br>
 * @see Request#getBodyContentType()
 */
@Override
public String getBodyContentType() {
    return this.mHttpEntity.getContentType().getValue();
}

deliverResponse

リクエストのコンテンツをパースし終わった値をリスナーに返却する

/**
 * Subclasses must implement this to perform delivery of the parsed<br>
 * response to their listeners.  The given response is guaranteed to<br>
 * be non-null; responses that fail to parse are not delivered.<br>
 *
 * @param response The parsed response returned by
 * {@link #parseNetworkResponse(NetworkResponse)}
 * @see Request#deliverResponse(Object)
 */
@Override
protected void deliverResponse(T response) {
    this.mVolleyResponseListener.onResponse(response);
}

getBody

POST PUT 時に送られる Body データーを MultipartEntityBuilder から取得する

/**
 * Returns the raw POST or PUT body to be sent.
 *
 * <p>By default, the body consists of the request parameters in<br>
 * application/x-www-form-urlencoded format. When overriding this method, consider overriding<br>
 * {@link #getBodyContentType()} as well to match the new body format.
 *
 * @throws AuthFailureError in the event of auth failure
 * @see Request#getBody()
 */
@Override
public byte[] getBody() throws AuthFailureError {
    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

    this.mHttpEntity = this.mMultipartEntityBuilder.build();

    try {
        this.mHttpEntity.writeTo(byteArrayOutputStream);
    } catch (IOException e) {
        e.printStackTrace();
    }

    return byteArrayOutputStream.toByteArray();
    }

使用例

Response.Listener<String> listener = new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // リクエスト成功時
    }
};

Response.ErrorListener errorListener = new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        // リクエスト失敗時
    }
};

MultipartRequest<String> multipartRequest = new MultipartRequest<String>("https://www.google.co.jp", requestStringBody, requestFileBody, requestFileName, listener, errorListener) {
    @Override
    protected Response<String> parseNetworkResponse(NetworkResponse response) {
        String parsed;
        try {
            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
        } catch (UnsupportedEncodingException e) {
            parsed = new String(response.data);
        }
        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
    }
};

RequestQueue requestQueue = Volley.newRequestQueue(this.getActivity());
requestQueue.add(multipartRequest);

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6