一、使用的注解搭配
方法 | 参数 |
---|---|
@POST+@Multipart |
@Part 或@PartMap |
@Post |
@Body |
二、相关注解
1、@Part
表单字段和@Multipart
结合。
类型支持
MultiPartBody.Part
-->内容会直接使用RequestBody
-->会使用RequestBody#contentType
- 任意类型-->会通过
retrofit#gsonConvert
转换json
结构。
kotlin
public @interface Part {
/**
* The name of the part. Required for all parameter types except {@link
* okhttp3.MultipartBody.Part}.
*/
String value() default "";
/** The {@code Content-Transfer-Encoding} of this part. */
String encoding() default "binary";
}
除参数类型为okhttp3.MultipartBody.Part
,都需要声明value;默认编码为binary
注意点
- 参数可为空,为空时,将会忽略,请求体中不会包含该字段。
- 需注意在除
MultPartBody.Part
参数类型之外,声明part
名称。
2、@PartMap
表单字段和@Multipart
结合。
类型支持
RequestBody
-->会使用RequestBody#contentType
- 任意类型-->会通过
retrofit#gsonConvert
对象类型转换json
结构
kotlin
public @interface PartMap {
/** The {@code Content-Transfer-Encoding} of the parts. */
String encoding() default "binary";
}
- 一般常规写法使用
Map<K,RequestBody>
注意点
Map
中的<k,v>
均不能为空,否则会报错(IllegalArgumentException
),K 必须为String
类型。- 在
Kolitn
中Map<K,@JvmSuppressWildcards RequestBody>
,注意需要声明@JvmSuppressWildcards
注解,否则会报错。
3、@Body
kotlin
public @interface Body {}
目前发现的用法,下述两种:
RequestBody
-->会使用RequestBody#contentType
- 任意类型-->会通过
retrofit#gsonConvert
对象类型转换json
结构,作为请求体直接发送。
注意点
- 参数不能为空,否则会报错。
三、文件上传实战
1、@POST
,@Multipart
,@Part
或@PartMap
kotlin
# Service
@Post(host/path)
@Multipart
suspend fun syncParamsFiles(
@Part("user_name") userName:String,
@Part filePart:MulitPartBody.Part,
@PartMap params:Map<String,@JvmSuppressWildcards RequestBody)
)
# Repository
val userName="that's it"
val fileRequestBody=RequestBody.create(MediaType.parse("application/octet-stream"),File(filepath))
val filePart=MultipartBody.Part.createFormData(
"file",
file.nameWithoutExtension,
fileRequestBody
)
val params=HashMap<String,RequestBody>()
params["gender"]=RequestBody.create(MediaType.parse("text/plain"),"female")
#invoke
apiService.syncParamsFiles(
userName,
filePart,
params
)
2、@Post
,@Body
kotlin
# Service
@Post(host/path)
suspend fun syncParamsFiles(
@Body requestBody:RequestBody
)
# Repository
val fileBody=RequestBody.create(MediaType.parse("application/octet-stream"), File(filepath))
val requestBody=MultipartBody.Builder()
.setType(MultipartBody.FORM)
.apply {
addFormDataPart("user_name", userName)
addFormDataPart("gender", userGender)
addFormDataPart(
"file",
file.nameWithoutExtension,
fileRequestBody
)
}.build()
# invoke
apiService.syncParamsFiles(requestBody)
四、实现带进度的文件上传
实现思路
- 委托
- 重写
RequestBody
以委托为例,实现文件上传的进度监听:
kotlin
open class ProgressRequestBody(
private val delegate:RequestBody,
private val onRequestProgressListener: OnRequestProgressListener
):RequestBody() {
override fun contentType(): MediaType?=delegate.contentType()
override fun contentLength(): Long {
try {
return delegate.contentLength()
}catch (ioException:IOException){
ioException.printStackTrace()
}
return -1
}
@Throws(IOException::class)
override fun writeTo(sink: BufferedSink) {
val countingSink=CountingSink(sink,contentLength(),onRequestProgressListener)
val bufferedSink=Okio.buffer(countingSink)
delegate.writeTo(bufferedSink)
bufferedSink.flush()
}
protected class CountingSink(
delegateSink:Sink,
val contentLength: Long,
private val onRequestProgressListener: OnRequestProgressListener
):ForwardingSink(delegateSink){
private var bytesWritten:Long=0L
override fun write(source: Buffer, byteCount: Long) {
super.write(source, byteCount)
bytesWritten+=byteCount
onRequestProgressListener.onRequestProgress(bytesWritten,contentLength)
}
}
interface OnRequestProgressListener{
fun onRequestProgress(
bytesWritten:Long,
contentLength:Long
)
}
}
# Repository 以3.2为例
val fileRequestBody = ProgressRequestBody(RequestBody.create(MediaType.parse("application/octet-stream"), File(filepath)),
object : ProgressRequestBody.OnRequestProgressListener {
override fun onRequestProgress(bytesWritten: Long, contentLength: Long) {
Log.d("junit", "onRequestProgress: bytesWritten->$bytesWritten," +
"contentLength->$contentLength,progress->${bytesWritten.times(1.0f)/contentLength}")
//dosomething
}
}
)
val requestBody=MultipartBody.Builder()
.setType(MultipartBody.FORM)
.apply {
addFormDataPart("user_name", userName)
addFormDataPart("gender", userGender)
addFormDataPart(
"file",
file.nameWithoutExtension,
fileRequestBody
)
}.build()
# invoke
apiService.syncParamsFiles(requestBody)
ENDING
That's it!