簡體   English   中英

使用 Retrofit 2 在對象數組中上傳文件

[英]Uploading a file in an array of object using Retrofit 2

我使用 Retrofit2,我需要使用對象Media數組中的file上傳各種文件,如下所示:

{
   "state" = "done",
   "medias" = [
     {
      "file" = THE_FILE1
     },
     {
      "file" = THE_FILE2
     },
     {
      "file" = THE_FILE3
     }
   ]
}

這是我的Interface的功能:

@Multipart
@POST("api/exercice/{id}")
fun submitExercice(
    @Path("id") id: Int,
    @Header("Authorization") token: String,
    @Body data: AnswerExercice
): Call<Void>

這是我的對象Media

    data class AnswerExercice(
    val state: String = "done",
    val medias: List<Media>
) : Serializable {
    data class Media(
        @Part val file: MultipartBody.Part
    ) : Serializable
}

但我有這個錯誤:

@Body 參數不能與表單或多部分編碼一起使用。 (參數#3)

我做的不好嗎?

API 文檔是這樣說的: 達

結果必須是這樣的: 在此處輸入圖像描述

解決方案 1
如果您想像您提到的結構一樣發送數據,您應該將文件內容轉換為Base64並將它們包裝在一個可序列化的類中並將其作為正文發布。 這是包裝類的示例:

data class AnswerExerciceBase64(val state: String, val medias: List<Media>) : Serializable

data class Media(val file: Base64File) : Serializable

class Base64File(file: File) : Serializable {

    val name: String
    val content: String

    init {
        name = file.name
        content = Base64.encodeToString(FileInputStream(file).readBytes(), Base64.DEFAULT)
    }
}

而你的Api是這樣的:

@POST("api/exercice/{id}")
fun submitExercice(
        @Path("id") id: Int,
        @Header("Authorization") token: String,
        @Body data: AnswerExerciceBase64
): Call<Void>

然后將數據發布到服務器將如下所示:

{
    "state": "this is state",
    "medias": [{
        "file": {
            "content": "Base64 file content",
            "name": "f1.txt"
        }
    }, {
        "file": {
            "content": "Base64 file content",
            "name": "f2.txt"
        }
    }, {
        "file": {
            "content": "Base64 file content",
            "name": "f3.txt"
        }
    }]
}

這種方法非常接近你想要的,但你應該知道你必須自己在服務器端解碼文件內容,所以你需要在服務器端做更多的工作。


方案二
最好使用multipart/form-data來上傳文件和數據。 基於“是否可以在多部分 POST 中嵌套 MultipartEntities 或 FormBodyPart?” 問題及其答案, multipart/form-data具有平面結構並且沒有層次結構,因此您不能擁有所需的數據結構,但您仍然可以通過單個對象將所有輸入傳遞給Api
根據這篇文章,您可以在一個列表中發送多個文件,所以如果您的Api是這樣的

@Multipart
@POST("post")
fun submitExercice(@Part data: List<MultipartBody.Part>): Call<ResponseBody>

那么您將能夠上傳多個文件。 您只需要創建一個MultipartBody.Part列表並將您的文件添加到其中,如下所示:

list.add(MultipartBody.Part.createFormData(name, fileName, RequestBody.create(mediaType, file)))

現在您必須將state參數添加到此列表中。 你可以這樣做:

list.add(MultipartBody.Part.createFormData("state", state))

我開發了一個類來處理所有這些東西。 你可以使用它。

class AnswerExerciceList(state: String) : ArrayList<MultipartBody.Part>() {

    init {
        add(MultipartBody.Part.createFormData("state", state))
    }

    fun addFile(name: String, fileName: String, mediaType: MediaType?, file: File) {
        add(MultipartBody.Part.createFormData(name, fileName,
                RequestBody.create(mediaType, file)))
    }
}

您可以創建此類的一個實例,添加您的文件,然后將其作為輸入傳遞給submitExercice Api 方法。

更新
此答案基於您的 Api 文檔。 我通過https://postman-echo.com測試了我的答案和你在問題中提到的例子,結果是一樣的。 請嘗試以下代碼片段: Api

@Multipart
@POST("api/exercice/{id}")
fun submitExercice(@Path("id") id: Int,
                   @Header("Authorization") authorization: String,
                   @Part("answer") answer: String,
                   @Part medias: List<MultipartBody.Part>,
                   @Part("state") state: String): Call<ResponseBody>

媒體類

data class Media(val urlVidel: String, val file: File?, val mediaType: MediaType?) {
    companion object {
        fun mediaListToMultipart(mediaList: List<Media>): List<MultipartBody.Part> {
            val list = ArrayList<MultipartBody.Part>()
            for (i in mediaList.indices) {
                mediaList[i].let {
                    if (!TextUtils.isEmpty(it.urlVidel))
                        list.add(MultipartBody.Part.createFormData("medias[$i][urlVideo]", it.urlVidel))
                    if (it.file != null) {
                        val requestFile = RequestBody.create(
                                it.mediaType,
                                it.file
                        )
                        list.add(MultipartBody.Part.createFormData("medias[$i][file]", it.file.getName(), requestFile))
                    }
                }
            }
            return list
        }
    }
}

然后像這樣調用 Api:

ApiHelper.Instance.submitExercice(1, "Authorization Token", "Answer", Media.mediaListToMultipart(mediaList), "State").enqueue(callback)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM