OkHttp初探3:简单文件上传表单文件一起上传带进度条的文件上传MediaType介绍。Kotlin版本
Posted pumpkin的玄学
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OkHttp初探3:简单文件上传表单文件一起上传带进度条的文件上传MediaType介绍。Kotlin版本相关的知识,希望对你有一定的参考价值。
相关博文:
前言
通用模块封装
这里封装一些通用的代码,知道一下就可以了。
/**
* 上传状态机
*/
sealed class UploadState
/**
* 未开始
*/
object UnStart : UploadState()
/**
* 文件不存在
*/
object FileNotExist : UploadState()
/**
* 上传完成
*/
object Complete : UploadState()
/**
* 上传中
*/
class Progress(var totalNum: Long, var current: Long) : UploadState()
/**
* 失败
*/
class Error(val e: Exception) : UploadState()
MediaType介绍
相信大多数人在写文件上传下载代码的时候,都不太明白MediaType
的含义。这里详细列出MediaType
含义。以及对应解释说明。
类型 | 描述 |
---|---|
text/html | HTML格式 |
text/plain | 纯文本格式,空格转换为 “+” 加号,不对特殊字符编码 |
text/xml | XML格式 |
text/x-markdown | Markdown格式 |
image/gif | gif图片格式 |
image/jpeg | jpg图片格式 |
image/png | png图片格式 |
application/xhtml+xml | XHTML格式 |
application/xml | XML数据格式 |
application/json | 用来告诉服务端,消息主体是序列化后的JSON字符串 |
application/pdf | pdf格式 |
application/msword | Word文档格式 |
application/octet-stream | 二进制流数据 |
application/x-www-form-urlencoded | 参数为键值对形式,在发送前编码所有字符(默认)。如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据 |
multipart/form-data | 不对字符编码,发送大量二进制数据或包含non-ASCII字符的文本,application/x-www-form-urlencoded是效率低下的(需要用更多字符表示一个non-ASCII字符)。需要设定“ <form enctype=‘multipart/form-data’ |
MediaType对象解析
text/html; charset=utf-8
//解析
type值是text,表示是文本这一大类;
后面的html是子类型,表示是文本这一大类下的html类型;
charset=utf-8 则表示采用UTF-8编码
上面介绍完了,下面正式代码封装就开始了。坐稳了,发车!
简单的文件上传
inline fun simpleUploadFile(
url: String,
filePath: String,
contentType: MediaType? = null,
crossinline block: (UploadState) -> Unit
)
var state: UploadState = UploadState.UnStart
block(state)
val file = File(filePath)
if (!file.exists())
//文件不存在则不上传
state = UploadState.FileNotExist
block(state)
return
val request = Request.Builder()
.url(url)
.post(file.asRequestBody(contentType))
.build()
val client = OkHttpClient.Builder()
.dispatcher(dispatcher)
.writeTimeout(30, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.MINUTES)
.connectTimeout(70, TimeUnit.SECONDS)
.build()
client.newCall(request).enqueue(object : Callback
override fun onFailure(call: Call, e: IOException)
log(e.message)
override fun onResponse(call: Call, response: Response)
if (response.isSuccessful)
state = UploadState.Complete
block(state)
else
log("请求失败")
)
表单和文件一起上传
inline fun multipartUpload(
url: String,
filePath: String,
contentType: MediaType? = null,
params: Map<String, String>? = null,
crossinline block: (UploadState) -> Unit
)
var state: UploadState = UploadState.UnStart
block(state)
val file = File(filePath)
val body = MultipartBody.Builder()
.also
params?.forEach (k, v) ->
it.addFormDataPart(k, v)
.also
if (file.exists())
it.addFormDataPart("filename", file.name, file.asRequestBody(contentType))
.build()
val request = Request.Builder()
.url(url)
.post(body)
// .addHeader() 可以增加请求头
.build()
val client = OkHttpClient.Builder()
.dispatcher(dispatcher)
.writeTimeout(30, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.MINUTES)
.connectTimeout(70, TimeUnit.SECONDS)
.build()
client.newCall(request).enqueue(object : Callback
override fun onFailure(call: Call, e: IOException)
log(e.message)
override fun onResponse(call: Call, response: Response)
if (response.isSuccessful)
state = UploadState.Complete
block(state)
else
log("请求失败")
)
文件上传带进度条,重点重写RequestBody
fun multipartUploadProgress(
url: String,
filePath: String,
contentType: MediaType? = null,
params: Map<String, String>? = null,
block: (UploadState) -> Unit
)
var state: UploadState = UploadState.UnStart
block(state)
val file = File(filePath)
val body = MultipartBody.Builder()
.also
params?.forEach (k, v) ->
it.addFormDataPart(k, v)
.also
if (file.exists())
it.addFormDataPart("filename", file.name, file.asProgressRequestBody(contentType, block))
.build()
val request = Request.Builder()
.url(url)
.post(body)
// .addHeader() 可以增加请求头
.build()
val client = OkHttpClient.Builder()
.dispatcher(dispatcher)
.writeTimeout(30, TimeUnit.MINUTES)
.readTimeout(30, TimeUnit.MINUTES)
.connectTimeout(70, TimeUnit.SECONDS)
.build()
client.newCall(request).enqueue(object : Callback
override fun onFailure(call: Call, e: IOException)
log(e.message)
override fun onResponse(call: Call, response: Response)
if (response.isSuccessful)
state = UploadState.Complete
block(state)
else
log("请求失败")
)
/**
* 带进度条上传的功能
*/
private fun File.asProgressRequestBody(contentType: MediaType? = null, block: (UploadState) -> Unit?): RequestBody
return object : RequestBody()
override fun contentType() = contentType
override fun contentLength() = length()
override fun writeTo(sink: BufferedSink)
source().use source ->
val buffer = Buffer()
var readCount = 0L
var progress = 0L
val progressBlock = UploadState.Progress(contentLength(), progress)
try
do
if (readCount != 0L)
progress += readCount
progressBlock.current = progress
sink.write(buffer, readCount)
block(progressBlock)
readCount = source.read(buffer, 2048)
while (readCount != -1L)
catch (e: Exception)
// e.printStackTrace()
block(UploadState.Error(e))
使用
multipartUploadProgress(
"https://api.github.com/markdown/raw",
"download/WeChatSetup.exe",
"application/octet-stream".toMediaType(),
mapOf(
"name" to "blog",
"auto_init" to "true",
"private" to "true",
"gitignore_template" to "nanoc"
)
) s ->
when (s)
UploadState.Complete ->
log("上传完成")
UploadState.FileNotExist ->
log("上传失败,文件不存在")
is UploadState.Progress ->
log("上传中 $(s.current.toFloat() / s.totalNum) * 100%")
UploadState.UnStart ->
log("上传未开始")
is UploadState.Error ->
log("上传失败 $s.e.message")
后面会陆续推出OkHttp高阶使用,以及OkHttp源码分析博客。觉得不错关注博主哈~😎
感兴趣可以查看博主之前的kotlin系列、kotlin协程flow、jetpack系列博客哈。
创作不易,如有帮助一键三连咯🙆♀️。欢迎技术探讨噢!
以上是关于OkHttp初探3:简单文件上传表单文件一起上传带进度条的文件上传MediaType介绍。Kotlin版本的主要内容,如果未能解决你的问题,请参考以下文章