使用Retrofit 2.0上传包括图片在内的多部分表单数据

199

我正在尝试使用Retrofit 2.0进行 HTTP POST 请求到服务器

MediaType MEDIA_TYPE_TEXT = MediaType.parse("text/plain");
MediaType MEDIA_TYPE_IMAGE = MediaType.parse("image/*");

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    imageBitmap.compress(Bitmap.CompressFormat.JPEG,90,byteArrayOutputStream);
profilePictureByte = byteArrayOutputStream.toByteArray();

Call<APIResults> call = ServiceAPI.updateProfile(
        RequestBody.create(MEDIA_TYPE_TEXT, emailString),
        RequestBody.create(MEDIA_TYPE_IMAGE, profilePictureByte));

call.enqueue();

服务器返回一个错误,说这个文件无效。

这很奇怪,因为我曾经使用其他库在iOS上尝试过以相同格式上传同样的文件,但上传成功了。

我想知道使用Retrofit 2.0上传图片的正确方式是什么?

我应该先将其保存到磁盘再上传吗?

P.S.:我已经使用Retrofit进行了其他Multipart请求,它们都成功完成了。问题出在当我试图将字节包含在请求体中时。


https://github.com/square/retrofit/issues/1217 - Raghunandan
http://square.github.io/retrofit/2.x/retrofit/retrofit2/http/Query.html - Daniel Viglione
13个回答

2

Kotlin版本更新RequestBody.create的方法:

Retrofit接口

@Multipart
@POST("uploadPhoto")
fun uploadFile(@Part file: MultipartBody.Part): Call<FileResponse>

并上传

fun uploadFile(fileUrl: String){
    val file = File(fileUrl)
    val fileUploadService = RetrofitClientInstance.retrofitInstance.create(FileUploadService::class.java)
    val requestBody = file.asRequestBody(file.extension.toMediaTypeOrNull())
    val filePart = MultipartBody.Part.createFormData(
        "blob",file.name,requestBody
    )
    val call = fileUploadService.uploadFile(filePart)

    call.enqueue(object: Callback<FileResponse>{
        override fun onFailure(call: Call<FileResponse>, t: Throwable) {
            Log.d(TAG,"Fckd")
        }

        override fun onResponse(call: Call<FileResponse>, response: Response<FileResponse>) {
            Log.d(TAG,"success"+response.toString()+" "+response.body().toString()+"  "+response.body()?.status)
        }

    })
}

感谢@jimmy0251的支持。

0
在我的情况下,我需要发送一个PDF文件(application/pdf),以及JSON信息(application/json)。幸运的是,Retrofit2使这变得非常简单。
我的接口如下所示:
interface MyApi {
    @Multipart
    @POST("upload")
    fun uploadPDF(
        @Part file: MultipartBody.Part,
        @Part(value = "jsoninfo") jsoninfo: MyJsonObject
    ): Call<MyResponse>
}

jsoninfo 是我的 JSON 数据名称,MyJsonObject 是我的数据类,MyResponse 则是我期望得到的响应。

然后,我只需要按照以下方式调用我的 API 方法:

val myJsonObject = MyJsonObject(...)

// "file" is of type byte[] already
val requestBody = RequestBody.create(file, MediaType.parse("application/pdf"))
val filePart = MultipartBody.Part.createFormData("file", "myfile.pdf", requestBody)

api.uploadPDF(filePart, myJsonObject).enqueue(...)

0

在函数名中不要使用多个参数,只需采用简单的几个参数约定即可提高代码的可读性,例如:

// MultipartBody.Part.createFormData("partName", data)
Call<SomReponse> methodName(@Part MultiPartBody.Part part);
// RequestBody.create(MediaType.get("text/plain"), data)
Call<SomReponse> methodName(@Part(value = "partName") RequestBody part); 
/* for single use or you can use by Part name with Request body */

// add multiple list of part as abstraction |ease of readability|
Call<SomReponse> methodName(@Part List<MultiPartBody.Part> parts); 
Call<SomReponse> methodName(@PartMap Map<String, RequestBody> parts);
// this way you will save the abstraction of multiple parts.

在使用Retrofit时,您可能会遇到多个异常,所有这些异常都记录在代码中,有一个步骤指南可以查看retrofit2/RequestFactory.java。您可以使用两个函数parseParameterAnnotationparseMethodAnnotation,在这里您可以处理异常,请仔细阅读,它将比谷歌/stackoverflow搜索节省更多时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接