Android 文件分段上傳和下載實(shí)現(xiàn)方案
一、背景
Android 中的大文件下載需要使用分段下載,下載通常是在線程中進(jìn)行的,假如有5段,那同時(shí)5個(gè)線程去執(zhí)行下載,請(qǐng)求http返回文件流后,需要將多個(gè)文件流同時(shí)寫進(jìn)同一個(gè)文件,這里用到
RandomAccessFile
分段上傳的話,只需要根據(jù)每段文件閥值,例如,50M為一段,將文件按照設(shè)置的閥值,分段上傳即可
二、相關(guān)代碼
2.1 分段上傳關(guān)鍵代碼
忽略網(wǎng)絡(luò)請(qǐng)求和狀態(tài)碼,每個(gè)人接口定義的的請(qǐng)求參數(shù)和返回code不一樣
private val DEFAULT_BLOCK_SIZE: Long = 50 * 1024 * 1024 //50MB val blockSize=DEFAULT_BLOCK_SIZE val randomAccessFile = RandomAccessFile(filePath, "r") val fileLen = randomAccessFile.length() //超過(guò)設(shè)定的單個(gè)文件大小,需要分塊上傳 val blockFileNum = Math.ceil((fileLen / blockSize.toDouble())).toInt() XLogUtil.d("${TAG}blockFileNum:$blockFileNum,,,,fileLen:$fileLen,,,blockSize:$blockSize,,,requestId:$requestId") var offSet = 0L var successNum = 0 var isSendResult = true for (i in 0 until blockFileNum) { val startOffset = i * blockSize val blockFileLen = Math.min(blockSize, fileLen - startOffset) val fileData = getFileData(filePath, offSet, blockFileLen.toInt()) // 創(chuàng)建文件名請(qǐng)求體 val requestBody = RequestBody.create(null, fileData) val call = RetrofitClient.getUploadFileService( token, requestId, offSet.toString(), uploadType ).uploadFile(file.name, requestBody) XLogUtil.d("${TAG}upload 第${i + 1}塊 block file,offSet:$offSet,,,blockFileLen:$blockFileLen,,,blockFileNum:$blockFileNum,,,fileLen:$fileLen,,,filePath:$filePath,,,fileData size:${fileData?.size},,,requestId:$requestId") offSet += blockFileLen call.enqueue(object : Callback<ResponseBody?> { override fun onResponse( call: Call<ResponseBody?>, response: Response<ResponseBody?> ) { val code = response.code() XLogUtil.d("${TAG}upload 第${i + 1}塊 block file result code:$code,,,requestId:$requestId") if (code == 201) { //處理成功響應(yīng) successNum++ if (successNum == blockFileNum) { XLogUtil.d("${TAG}upload all block file success,blockFileNum:$blockFileNum,,,requestId:$requestId") listener?.apply { onSuccess(Constant.SUCCESS, requestId) } //上傳完 } } else { //處理失敗響應(yīng) } } override fun onFailure(call: Call<ResponseBody?>, t: Throwable) { // 處理請(qǐng)求失敗 XLogUtil.d("${TAG}upload 第${i + 1}塊 block file onFailure message:${t.printStackTrace()}") } }) } /** * 根據(jù)偏移量獲取分塊文件數(shù)據(jù) */ fun getFileData(filePath: String, offset: Long, length: Int): ByteArray? { // 使用RandomAccessFile隨機(jī)訪問(wèn)文件 var randomAccessFile: RandomAccessFile? = null try { randomAccessFile = RandomAccessFile(filePath, "r") val fileChannel = randomAccessFile.channel // 將文件的部分區(qū)域映射為內(nèi)存區(qū)域 val mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, length.toLong()) val data = ByteArray(length) // 從映射區(qū)域中讀取數(shù)據(jù) mappedByteBuffer[data] return data } catch (e: Exception) { e.printStackTrace() return null } finally { if (randomAccessFile != null) { try { randomAccessFile.close() } catch (e: Exception) { e.printStackTrace() } } } }
2.2、分段下載關(guān)鍵代碼
val folder = File(DOWNLOAD_FOLDER_PATH) if (!folder.exists()) { folder.mkdirs() } val fileName = getFileNameWithPath(filePath) val downFile = File(DOWNLOAD_FOLDER_PATH + File.separator + fileName) if (!downFile.exists()) { downFile.createNewFile() } // 使用輸入流保存響應(yīng)體到文件,這里通常是通過(guò)http請(qǐng)求,返回的文件流,替換即可 val inputStream = body.byteStream() val rw = RandomAccessFile(downFile, "rw") rw.seek(startPosition)//文件寫入的初始位置 var hasReads = 0 var readLenght: Long = 0 val bytes = ByteArray(4096) while ((inputStream.read(bytes).also { hasReads = it }) > 0) { rw.write(bytes, 0, hasReads) readLenght += hasReads // val l = (readLenght * 100 / contentLength) as Int 單塊文件寫入進(jìn)度 } // 關(guān)閉文件輸出流和輸入流 inputStream.close() rw.close() /** * 根據(jù)文件路徑獲取文件名 */ fun getFileNameWithPath(path: String): String { if (TextUtils.isEmpty(path)) { return "" } val start = path.lastIndexOf("/") return if (start != -1) { path.substring(start + 1) } else { "DEFAULT_NAME" } }
到此這篇關(guān)于Android 文件分段上傳和下載實(shí)現(xiàn)方案的文章就介紹到這了,更多相關(guān)Android 文件分段上傳和下載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android自定義Adapter的ListView的思路及代碼
Android自定義Adapter的ListView的思路及代碼,需要的朋友可以參考一下2013-05-05使用android studio開(kāi)發(fā)工具編譯GBK轉(zhuǎn)換三方庫(kù)iconv的方法
這篇文章主要介紹了使用android studio開(kāi)發(fā)工具編譯GBK轉(zhuǎn)換三方庫(kù)iconv的教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06Kotlin標(biāo)準(zhǔn)函數(shù)與靜態(tài)方法基礎(chǔ)知識(shí)詳解
Kotlin中的標(biāo)準(zhǔn)函數(shù)指的是Standard.kt文件中定義的函數(shù),任何Kotlin代碼都可以自由地調(diào)用所有的標(biāo)準(zhǔn)函數(shù)。例如let這個(gè)標(biāo)準(zhǔn)函數(shù),他的主要作用就是配合?.操作符來(lái)進(jìn)行輔助判空處理2022-11-11Android定時(shí)開(kāi)機(jī)的流程詳解
這篇文章給大家分享了Android定時(shí)開(kāi)機(jī)及其實(shí)現(xiàn)流程,對(duì)此知識(shí)點(diǎn)有興趣的朋友,可以學(xué)習(xí)參考下。2018-07-07Android自定義Dialog實(shí)現(xiàn)通用圓角對(duì)話框
這篇文章主要為大家詳細(xì)介紹了Android自定義Dialog實(shí)現(xiàn)通用圓角對(duì)話框,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11Android程序開(kāi)發(fā)之ListView 與PopupWindow實(shí)現(xiàn)從左向右滑動(dòng)刪除功能
這篇文章主要介紹了Android程序開(kāi)發(fā)之ListView 與PopupWindow實(shí)現(xiàn)滑動(dòng)刪除功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07安裝android開(kāi)發(fā)環(huán)境原始版(windows版)
安裝android開(kāi)發(fā)環(huán)境原始版(windows版)的詳細(xì)步驟2013-03-03