Android 100行代码搞定okhttp带进度上传 断点上传 分片上传 多文件上传

最近在做云备份功能。包含上传和下载。网上的断点上传太复杂了,不稳定,缺少重试机制等。我用最简单的方式交会你们


步骤和流程:

1.先分块,把块分好!

总大小/每一块的值 

2.md5是一块文件的md5还是

都要上传,获取字节数组的md5

3. 多并发处理网络请求

并行:CountDownLatch来监听结果

当使用多线程去下载或者上传时,由于多个线程互不干扰的执行,怎么判断所有的线程是否执行完毕呢?线程池没有提供这样的方法,那么只能自己去实现了。一般可以设置一个整形的标志位,初始化为0,当一个线程完成后就把这个标志位+1,然后判断标志位是否等于=任务的数量,等于就代表所有任务都执行完成了,但是这样感觉不是很优雅。java中提供了一个计数器,我们可以使用CountDownLatch来判断所有任务是否完成

串行:每次循环,他都给我返回,即使是成功的也是,那么20多块,用串行的方式,写同步请求,得到一个再用一个

4.进度计算   (已上传的块数*每个块数大小)

5.结束的判断!怎么知道全部上传完成了?   判断所有的块都上传成功了

6.重试机制                     添加计算计算

7.取消上传,有回调吗?是不是应该给取消接口

okhttp的取消回调

8.断点怎么实现的:      一块一块,上传的块不会再次上传

9.进程不在,怎么知道失败的个数和数量?

由后台返回数据才行。给我返回分块的总数,已经完成的个数!还有每一个的编号!

/**

*分片上传

*/

fun multiPartUpload(fileAccess: FileAccess, totalChunks: Int, chunkNumber: Int): UploadResult {

var uploadResult = UploadResult()

var uploadFile = File(transferItemModel.path)

val token = HttpRequestHelper.getToken()

var offset = chunkNumber.toLong() -1

    BNLog.d(TAG, "offset:" + offset)

var fileByte = fileAccess.getBlock(offset * FileAccess.CHUNK_SIZE, uploadFile)

var mutipartBody = MultipartBody.Builder()

mutipartBody.setType(MultipartBody.FORM)

mutipartBody.addFormDataPart("fileFolderId", transferItemModel.fileFolderId)

.addFormDataPart("uploadId", uploadId)

.addFormDataPart("totalChunks", totalChunks.toString())

.addFormDataPart("chunkNumber", chunkNumber.toString())

.addFormDataPart("isAutoComplete", 1.toString())

.addFormDataPart("identifier", MultiPartUtil.getBytesMd5(fileByte))

sumByte = fileByte.size +sumByte

    BNLog.d(TAG, "multiPartUpload 上传字节大小:" + fileByte.size +"当前已经上传的总大小:" +sumByte)

var body = RequestBody.create(MultipartBody.FORM, fileByte)

mutipartBody.addFormDataPart("file", uploadFile.name, body)

var request = Request.Builder().url(HttpPathEntity.HOST + HttpPathEntity.uploadPart.path)

.header("token", token)

.header("sign", HttpRequestHelper.DEFAULT_SIGN)

.header("deviceId", DeviceUtils.androidID)

.header("timestamp", System.currentTimeMillis().toString())

.post(mutipartBody.build())

.build()

var client = OkHttpClient()

call = client.newCall(request)

try {

var response =call.execute()

var responseBody = response.body()

if (response.isSuccessful) {

var content = response.body()?.string()

var result = JSONObject(content)

var resultCode = result.getInt("code")

var message = result.getString("message")

uploadResult.code = resultCode

uploadResult.msg = message

BNLog.d(TAG, " success responseBody:" + responseBody +"content:" + content)

}else {

BNLog.e(TAG, " fail responseBody:")

}

}catch (e: Exception) {

}

return uploadResult

}

suspend fun uploadFile() {

BNLog.d(TAG, "uploadFile filePath = " +transferItemModel.path +"threadName:" + Thread.currentThread().name)

var fileAccess = FileAccess(transferItemModel.path)

var totalChunks = fileAccess.chunkSize

        var retryTimes =DEFAULT_RETRY_TIME

        var isOk =false

        var successCount =0

        var totalFileByteSize = MultiPartUtil.getFileByteSize(transferItemModel.path)

BNLog.e(TAG, "文件的总长度:" +transferItemModel.fileSize +"  读取的总字节:" + totalFileByteSize)

while (retryTimes >0 && !isOk) {

for (iin 1 until (totalChunks +1)) {

BNLog.d(TAG, "uploadFile i=" + i)

if (i == totalChunks) {

BNLog.d(TAG, "上传最后一块:" + (totalChunks -1))

}else {

//                    continue

                }

var uploadResult = multiPartUpload(fileAccess, totalChunks, i)

if (uploadResult.code == UploadResult.SUCCESS_CODE) {//上传成功

                    successCount++

var currentSize = successCount * FileAccess.CHUNK_SIZE//todo

                    var progress = (100 * currentSize / totalFileByteSize).toInt()

BNLog.e(TAG, "progress:" + progress)

if (progress >100) {

progress =100

                    }

this.ossListener?.onProgress(currentSize.toLong(), totalFileByteSize, progress)

}

//                return//test

            }

isOk = checkIsUploadSuccess(totalChunks, successCount)

isOk =true//test

            retryTimes--

if (isOk) {

BNLog.d(TAG, "uploadFile 全部上传成功")

break

            }

}

retryTimes =0

        //上传完成,判断是否有失败的块

        if (isOk) {

BNLog.e(TAG, "一个文件上传成功:" +transferItemModel.path)

this.ossListener?.onSuccess()

this.ossListener?.onReport(true)

var baonengId = UserCenterManage.getInstance(CloudServiceApp.getInstance()).getRefreshToken()

var uuid =transferItemModel.uuid

            var uuidserver = baonengId +"/" + uuid

syncFileMeta(transferItemModel.fileFolderId, transferItemModel.name, uuidserver, transferItemModel.fileSize.toString(), transferItemModel.md5, transferItemModel.mineType)

}else {

this.ossListener?.onFailure(999, "fail")

}

}

/***

* 是否所有的块都成功了

*/

fun checkIsUploadSuccess(totalChunks: Int, successCount: Int): Boolean {

if (successCount >= totalChunks) {

return true

    }

return false

}

okhttp相关的:

问题:

1.okhttp取消请求有回调吗?

2.multipart/form-data是浏览器提交表单上传文件的一种方式。

.addFormDataPart("uploadfile", uploadfile, RequestBody.create(MediaType.parse("*/*"), file)) // 第一个参数传到服务器的字段名,第二个你自己的文件名,第三个MediaType.parse("*/*")和我们之前说的那个type其实是一样的

3、为什么response.body().string() 只能调用一次

我们可能习惯在获取到Response对象后,先response.body().string()打印一遍log,再进行数据解析,却发现第二次直接抛异常,其实直接跟源码进去看就发现,通过source拿到字节流以后,直接给closeQuietly悄悄关闭了,这样第二次再去通过source读取就直接流已关闭的异常了。

publicfinal Stringstring()throws IOException{BufferedSource source=source();try{Charset charset=Util.bomAwareCharset(source,charset());returnsource.readString(charset);}finally{//这里讲resource给悄悄close了Util.closeQuietly(source);}}

解决方案:1.内存缓存一份response.body().string();2.自定义拦截器处理log。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容