一、背景
Android 中的大文件下载必要使用分段下载,下载通常是在线程中举行的,如果有5段,那同时5个线程去实行下载,请求http返回文件流后,必要将多个文件流同时写进同一个文件,这里用到
分段上传的话,只必要根据每段文件阀值,例如,50M为一段,将文件按照设置的阀值,分段上传即可
二、相关代码
2.1 分段上传关键代码
忽略网络请求和状态码,每个人接口界说的的请求参数和返回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()
- //超过设定的单个文件大小,需要分块上传
- 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())
- // 创建文件名请求体
- 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) {
- //处理成功响应
- successNum++
- if (successNum == blockFileNum) {
- XLogUtil.d("${TAG}upload all block file success,blockFileNum:$blockFileNum,,,requestId:$requestId")
- listener?.apply {
- onSuccess(Constant.SUCCESS, requestId)
- }
- //上传完
-
- }
- } else {
- //处理失败响应
-
- }
- }
- override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
- // 处理请求失败
- XLogUtil.d("${TAG}upload 第${i + 1}块 block file onFailure message:${t.printStackTrace()}")
-
- }
- })
- }
- /**
- * 根据偏移量获取分块文件数据
- */
- fun getFileData(filePath: String, offset: Long, length: Int): ByteArray? {
- // 使用RandomAccessFile随机访问文件
- var randomAccessFile: RandomAccessFile? = null
- try {
- randomAccessFile = RandomAccessFile(filePath, "r")
- val fileChannel = randomAccessFile.channel
- // 将文件的部分区域映射为内存区域
- val mappedByteBuffer =
- fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, length.toLong())
- val data = ByteArray(length)
- // 从映射区域中读取数据
- 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、分段下载关键代码
- 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()
- }
- // 使用输入流保存响应体到文件,这里通常是通过http请求,返回的文件流,替换即可
- 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 单块文件写入进度
- }
- // 关闭文件输出流和输入流
- inputStream.close()
- rw.close()
- /**
- * 根据文件路径获取文件名
- */
- 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"
- }
- }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |