Android 100行代碼搞定okhttp帶進(jìn)度上傳 斷點(diǎn)上傳 分片上傳 多文件上傳

最近在做云備份功能系谐。包含上傳和下載。網(wǎng)上的斷點(diǎn)上傳太復(fù)雜了魂奥,不穩(wěn)定菠剩,缺少重試機(jī)制等。我用最簡(jiǎn)單的方式交會(huì)你們


步驟和流程:

1.先分塊耻煤,把塊分好具壮!

總大小/每一塊的值?

2.md5是一塊文件的md5還是

都要上傳,獲取字節(jié)數(shù)組的md5

3. 多并發(fā)處理網(wǎng)絡(luò)請(qǐng)求

并行:CountDownLatch來(lái)監(jiān)聽(tīng)結(jié)果

當(dāng)使用多線(xiàn)程去下載或者上傳時(shí),由于多個(gè)線(xiàn)程互不干擾的執(zhí)行哈蝇,怎么判斷所有的線(xiàn)程是否執(zhí)行完畢呢棺妓?線(xiàn)程池沒(méi)有提供這樣的方法,那么只能自己去實(shí)現(xiàn)了买鸽。一般可以設(shè)置一個(gè)整形的標(biāo)志位涧郊,初始化為0,當(dāng)一個(gè)線(xiàn)程完成后就把這個(gè)標(biāo)志位+1眼五,然后判斷標(biāo)志位是否等于=任務(wù)的數(shù)量妆艘,等于就代表所有任務(wù)都執(zhí)行完成了,但是這樣感覺(jué)不是很優(yōu)雅看幼。java中提供了一個(gè)計(jì)數(shù)器批旺,我們可以使用CountDownLatch來(lái)判斷所有任務(wù)是否完成

串行:每次循環(huán),他都給我返回诵姜,即使是成功的也是汽煮,那么20多塊,用串行的方式棚唆,寫(xiě)同步請(qǐng)求暇赤,得到一個(gè)再用一個(gè)

4.進(jìn)度計(jì)算? ?(已上傳的塊數(shù)*每個(gè)塊數(shù)大小)

5.結(jié)束的判斷宵凌!怎么知道全部上傳完成了鞋囊?? ?判斷所有的塊都上傳成功了

6.重試機(jī)制? ? ? ? ? ? ? ? ? ? ?添加計(jì)算計(jì)算

7.取消上傳,有回調(diào)嗎瞎惫?是不是應(yīng)該給取消接口

okhttp的取消回調(diào)

8.斷點(diǎn)怎么實(shí)現(xiàn)的:? ? ? 一塊一塊溜腐,上傳的塊不會(huì)再次上傳

9.進(jìn)程不在,怎么知道失敗的個(gè)數(shù)和數(shù)量瓜喇?

由后臺(tái)返回?cái)?shù)據(jù)才行挺益。給我返回分塊的總數(shù),已經(jīng)完成的個(gè)數(shù)乘寒!還有每一個(gè)的編號(hào)望众!

/**

*分片上傳

*/

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 上傳字節(jié)大小:" + fileByte.size +"當(dāng)前已經(jīng)上傳的總大小:" +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, "文件的總長(zhǎng)度:" +transferItemModel.fileSize +"? 讀取的總字節(jié):" + 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, "一個(gè)文件上傳成功:" +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相關(guān)的:

問(wèn)題:

1.okhttp取消請(qǐng)求有回調(diào)嗎?

2.multipart/form-data是瀏覽器提交表單上傳文件的一種方式黍檩。

.addFormDataPart("uploadfile", uploadfile, RequestBody.create(MediaType.parse("*/*"), file)) // 第一個(gè)參數(shù)傳到服務(wù)器的字段名叉袍,第二個(gè)你自己的文件名,第三個(gè)MediaType.parse("*/*")和我們之前說(shuō)的那個(gè)type其實(shí)是一樣的

3刽酱、為什么response.body().string() 只能調(diào)用一次

我們可能習(xí)慣在獲取到Response對(duì)象后喳逛,先response.body().string()打印一遍log,再進(jìn)行數(shù)據(jù)解析棵里,卻發(fā)現(xiàn)第二次直接拋異常润文,其實(shí)直接跟源碼進(jìn)去看就發(fā)現(xiàn),通過(guò)source拿到字節(jié)流以后殿怜,直接給closeQuietly悄悄關(guān)閉了典蝌,這樣第二次再去通過(guò)source讀取就直接流已關(guān)閉的異常了。

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

解決方案:1.內(nèi)存緩存一份response.body().string()头谜;2.自定義攔截器處理log骏掀。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市柱告,隨后出現(xiàn)的幾起案子截驮,更是在濱河造成了極大的恐慌,老刑警劉巖际度,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件葵袭,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乖菱,警方通過(guò)查閱死者的電腦和手機(jī)坡锡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)窒所,“玉大人鹉勒,你說(shuō)我怎么就攤上這事〕橙。” “怎么了禽额?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)海渊。 經(jīng)常有香客問(wèn)我,道長(zhǎng)哲鸳,這世上最難降的妖魔是什么臣疑? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮徙菠,結(jié)果婚禮上讯沈,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好缺狠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布问慎。 她就那樣靜靜地躺著,像睡著了一般挤茄。 火紅的嫁衣襯著肌膚如雪如叼。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,488評(píng)論 1 302
  • 那天穷劈,我揣著相機(jī)與錄音笼恰,去河邊找鬼。 笑死歇终,一個(gè)胖子當(dāng)著我的面吹牛社证,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播评凝,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼追葡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奕短?” 一聲冷哼從身側(cè)響起宜肉,我...
    開(kāi)封第一講書(shū)人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎篡诽,沒(méi)想到半個(gè)月后崖飘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡杈女,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年朱浴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片达椰。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翰蠢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出啰劲,到底是詐尸還是另有隱情梁沧,我是刑警寧澤,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布蝇裤,位于F島的核電站廷支,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏栓辜。R本人自食惡果不足惜恋拍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望藕甩。 院中可真熱鬧施敢,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至默怨,卻和暖如春讯榕,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背先壕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工瘩扼, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人垃僚。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓集绰,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谆棺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子栽燕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容