[英]Uploading a file in an array of object using Retrofit 2
I use Retrofit2 and I need to upload various files using file
in an array of objects Media
like this:我使用 Retrofit2,我需要使用对象
Media
数组中的file
上传各种文件,如下所示:
{
"state" = "done",
"medias" = [
{
"file" = THE_FILE1
},
{
"file" = THE_FILE2
},
{
"file" = THE_FILE3
}
]
}
This is the function of my Interface
:这是我的
Interface
的功能:
@Multipart
@POST("api/exercice/{id}")
fun submitExercice(
@Path("id") id: Int,
@Header("Authorization") token: String,
@Body data: AnswerExercice
): Call<Void>
And this is my object Media
:这是我的对象
Media
:
data class AnswerExercice(
val state: String = "done",
val medias: List<Media>
) : Serializable {
data class Media(
@Part val file: MultipartBody.Part
) : Serializable
}
But I have this error:但我有这个错误:
@Body parameters cannot be used with form or multi-part encoding.
@Body 参数不能与表单或多部分编码一起使用。 (parameter #3)
(参数#3)
What am i not doing well?我做的不好吗?
Solution 1解决方案 1
If you like to send your data exactly like the structure you mentioned, you should convert files content to Base64
and wrap them in a serializable class and post it as the body.如果您想像您提到的结构一样发送数据,您应该将文件内容转换为
Base64
并将它们包装在一个可序列化的类中并将其作为正文发布。 Here is the sample of wrapper class:这是包装类的示例:
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)
}
}
And your Api
is like this:而你的
Api
是这样的:
@POST("api/exercice/{id}")
fun submitExercice(
@Path("id") id: Int,
@Header("Authorization") token: String,
@Body data: AnswerExerciceBase64
): Call<Void>
Then posted data to server will be like below:然后将数据发布到服务器将如下所示:
{
"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"
}
}]
}
This approach is so close to what you want but you should know you must decode files content on the server-side by yourself, so you need more effort on the server-side.这种方法非常接近你想要的,但你应该知道你必须自己在服务器端解码文件内容,所以你需要在服务器端做更多的工作。
Solution 2方案二
It's better to use multipart/form-data
to upload files and data.最好使用
multipart/form-data
来上传文件和数据。 Based on "Is it possible to have a nested MultipartEntities or FormBodyPart in a multipart POST?"基于“是否可以在多部分 POST 中嵌套 MultipartEntities 或 FormBodyPart?” question and its answer,
multipart/form-data
has a flat structure and there is no hierarchy, so you can't have desired data structure but you can still pass all of the inputs to Api
through a single object.问题及其答案,
multipart/form-data
具有平面结构并且没有层次结构,因此您不能拥有所需的数据结构,但您仍然可以通过单个对象将所有输入传递给Api
。
According to this article , you can send multiple files in a List, so if your Api
be like this根据这篇文章,您可以在一个列表中发送多个文件,所以如果您的
Api
是这样的
@Multipart
@POST("post")
fun submitExercice(@Part data: List<MultipartBody.Part>): Call<ResponseBody>
then you will be able to upload multiple files.那么您将能够上传多个文件。 You just need to create a List of
MultipartBody.Part
and add your files to it like below:您只需要创建一个
MultipartBody.Part
列表并将您的文件添加到其中,如下所示:
list.add(MultipartBody.Part.createFormData(name, fileName, RequestBody.create(mediaType, file)))
Now you must add the state
parameter to this list.现在您必须将
state
参数添加到此列表中。 You can do it like this:你可以这样做:
list.add(MultipartBody.Part.createFormData("state", state))
I developed a class that handles all this stuff.我开发了一个类来处理所有这些东西。 You can use it.
你可以使用它。
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)))
}
}
You can create an instance of this class, add your files and then pass it to the submitExercice
Api method as input.您可以创建此类的一个实例,添加您的文件,然后将其作为输入传递给
submitExercice
Api 方法。
Update更新
This answer is based on your Api documnetation.此答案基于您的 Api 文档。 I tested my answer and the example that you mentioned in your question via
https://postman-echo.com
and result was the same.我通过
https://postman-echo.com
测试了我的答案和你在问题中提到的例子,结果是一样的。 Please try the following code snippet: Api请尝试以下代码片段: 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>
Media Class媒体类
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
}
}
}
and then call Api like this:然后像这样调用 Api:
ApiHelper.Instance.submitExercice(1, "Authorization Token", "Answer", Media.mediaListToMultipart(mediaList), "State").enqueue(callback)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.