3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Android、kotlon、RetrofitでHTTP multipart/form-dataでファイルを送信してみる

Posted at

HTTP POST multipart/form-dataでファイルを送信してみる

前回、RetrofitでJSON over HTTPで送受信をしてみましたが、今回はHTTP POST multipart/form-dataでファイルを送信してみます。

Serviceのインタフェース

ミソは@Post@Multipartアノテーションをメソッドに付けます。
引数の送信するファイルは@Partアノテーションを付けます。引数の型はokhttp3.MultipartBody.Partになります。

SampleService.kt
interface MediCodeSendService {
・・・

    @POST
    @Multipart
    suspend fun doUpload(
        @Url url: String,
        @Part part: MultipartBody.Part
    ): Response<Unit>
}

戻り値の型ですが、ファイルをアップロードする場合でHTTPのレスポンスBodyがない場合、Responseの型引数はUnitとなります。ファイルをアップロードは成功したか、失敗したかなのでHTTP Statusで足りるのでレスポンスBodyが何も返らない場合があります。

Retrofitの公式ではあまりこの辺は詳しくかかれていませんが(そもそもドキュメントが貧弱)、Retrofitの公式ページの FORM ENCODED AND MULTIPARTに書かれています。

Repositoryクラス

Repositoryクラスの初期化の部分は前回と同じです。ファイルアップロードのメソッドだけ追加します

SampleRepository.kt

class SampleRepository(baseUrl: String, timeout: Long) {
    /** サービス */
    private var service: SampleService

    init {
        // ロギング
        val logging = HttpLoggingInterceptor()
        logging.setLevel(HttpLoggingInterceptor.Level.BASIC)
        val client = OkHttpClient.Builder()
            .readTimeout(timeout, TimeUnit.SECONDS)
            .connectTimeout(timeout, TimeUnit.SECONDS)
            .addInterceptor(logging)
            .build()
        val moshi = Moshi.Builder()
            .add(KotlinJsonAdapterFactory())
            .build()
        service = Retrofit.Builder()
            .baseUrl(baseUrl)
            .client(client)
            .addConverterFactory(MoshiConverterFactory.create(moshi))
            .build()
            .create(SampleService::class.java)
    }
・・・

    suspend fun doUplaod(uploadUrl: String, datFile: File): Pair<String, String> {
        val part = MultipartBody.Part.createFormData(
            "data", "testData.csv", datFile.asRequestBody("text/csv".toMediaType())
        )
        val response = service.doUpload(uploadUrl, part)
    }

}

Repositoryクラスの中でFileからMultipart/form-dataに相当する、MultipartBody.Partのインスタンスを作ってやらないといけません。

val part = MultipartBody.Part.createFormData(
            "data", "testData.csv", datFile.asRequestBody("text/csv".toMediaType())

の部分がそれに相当しますが、ここはOkHttp3の機能なのでRetrofitの公式ページには触れられていません。
MultipartBody.Part.createFormDataの

  • 第一引数が、Content-Dispositionのname属性
  • 第二引数が、Content-Dispositionのfilename属性
  • 第三引数が、ファイルの実体、asRequestBody(・・・)が、Content-Type: text/csvに相当

となります。

Content-Disposition: form-data; name="data"; filename="testData.csv"
Content-Type: text/csv

HTTP Heaerを付けてみる

HTTP request headerを付けて送る方法はRetrofitの公式ページの HEADER MANIPULATIONに書かれています。

HTTP headerを1個だけ、固定で送信する場合

@Headers("Cache-Control: max-age=640000")
@GET("widget/list")
Call<List<Widget>> widgetList();

HTTP headerを複数、固定で送信する場合

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})
@GET("users/{username}")
Call<User> getUser(@Path("username") String username);

HTTP headerを1個だけ、引数に含めて可変で送信する場合

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

HTTP headerを複数、引数に含めて可変で送信する場合

@GET("user")
Call<User> getUser(@HeaderMap Map<String, String> headers)

response headerは戻り値のResponseから取得します。

val response = service.doUpload(uploadUrl, part)
response.headers().forEach {header ->  
    println("${header.first} = ${header.second}") // 全部
}
val hogeHeader = response.headers()["X-HOGE-HEADER"] ?: "" // 1個だけ狙い撃ち
3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?