一、背景
一般上传文件都是以表单形式上传文件,最近项目中涉及到非表单形式上传文件流,分为单个文件流上传、大文件分段上传,此种情景资料较少,这里记录下。
二、方案介绍
2.1 需求协议
1. 上传文件
API 端点:/service/upload
● 方法:PUT
● 请求参数:
○ filename:文件名(必选)
○ 请求头:
○ Authorization:用于身份验证的 token(必选)
○ X-Request-ID:请求的唯一标识符(必选)
○ Upload-Offset:当前上传块的偏移量(必选)
■ 单位:byte
○ Upload-Type:文件上传方式(必选)
■ 1 上传到相册(/storage/emulated/0/Pictures/)
■ 2 上传到sdcard(/sdcard/)
○ 请求体
○ 文件二进制数据
2.2 定义接口
格式:http://example.com/api/upload?filename=example.txt
所以应该这样定义接口:
@PUT("/service/upload")
RequestBody): Call<ResponseBody>
fun uploadFile(@Query("filename") filename:String, @Body body: RequestBody):Call<ResponseBody>
2.3 定义一个RequestInterceptor 迭代器,将请求参数放到请求头中
class RequestInterceptor(val authorization:String,val requestId:String,val offset:String,val uploadType:String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val requestBuilder = request.newBuilder()
requestBuilder.addHeader("Authorization", authorization)
requestBuilder.addHeader("X-Request-ID", requestId)
if(offset.isNotEmpty()){
requestBuilder.addHeader("Upload-Offset", offset)
}
if(uploadType.isNotEmpty()){
requestBuilder.addHeader("Upload-Type", uploadType)
}
// requestBuilder.removeHeader("Content-Length")
// requestBuilder.removeHeader("Content-Transfer-Encoding")
// requestBuilder.removeHeader("Content-Disposition")
return chain.proceed(requestBuilder.build())
}
}
2.4调用接口
// 创建文件名请求体
val requestBody = RequestBody.create(null, file)//第一个参数传null
val call = RetrofitClient.getUploadFileService(token, requestId, "0", uploadType)
.uploadFile(file.name, requestBody)
用到的相关库:
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.14.9'