Android 文件分段上传和下载方案

打印 上一主题 下一主题

主题 492|帖子 492|积分 1476

一、背景

Android 中的大文件下载必要使用分段下载,下载通常是在线程中举行的,如果有5段,那同时5个线程去实行下载,请求http返回文件流后,必要将多个文件流同时写进同一个文件,这里用到
  1. RandomAccessFile。
复制代码
分段上传的话,只必要根据每段文件阀值,例如,50M为一段,将文件按照设置的阀值,分段上传即可

二、相关代码

2.1 分段上传关键代码

忽略网络请求和状态码,每个人接口界说的的请求参数和返回code不一样

  1.        private val DEFAULT_BLOCK_SIZE: Long = 50 * 1024 * 1024 //50MB
  2.         val blockSize=DEFAULT_BLOCK_SIZE
  3.         val randomAccessFile = RandomAccessFile(filePath, "r")
  4.         val fileLen = randomAccessFile.length()
  5.         //超过设定的单个文件大小,需要分块上传
  6.             val blockFileNum = Math.ceil((fileLen / blockSize.toDouble())).toInt()
  7.             XLogUtil.d("${TAG}blockFileNum:$blockFileNum,,,,fileLen:$fileLen,,,blockSize:$blockSize,,,requestId:$requestId")
  8.             var offSet = 0L
  9.             var successNum = 0
  10.             var isSendResult = true
  11.             for (i in 0 until blockFileNum) {
  12.                 val startOffset = i * blockSize
  13.                 val blockFileLen = Math.min(blockSize, fileLen - startOffset)
  14.                 val fileData = getFileData(filePath, offSet, blockFileLen.toInt())
  15.                 // 创建文件名请求体
  16.                 val requestBody = RequestBody.create(null, fileData)
  17.                 val call = RetrofitClient.getUploadFileService(
  18.                     token,
  19.                     requestId,
  20.                     offSet.toString(),
  21.                     uploadType
  22.                 ).uploadFile(file.name, requestBody)
  23.                 XLogUtil.d("${TAG}upload 第${i + 1}块 block file,offSet:$offSet,,,blockFileLen:$blockFileLen,,,blockFileNum:$blockFileNum,,,fileLen:$fileLen,,,filePath:$filePath,,,fileData size:${fileData?.size},,,requestId:$requestId")
  24.                 offSet += blockFileLen
  25.                 call.enqueue(object : Callback<ResponseBody?> {
  26.                     override fun onResponse(
  27.                         call: Call<ResponseBody?>,
  28.                         response: Response<ResponseBody?>
  29.                     ) {
  30.                         val code = response.code()
  31.                         XLogUtil.d("${TAG}upload 第${i + 1}块 block file result code:$code,,,requestId:$requestId")
  32.                         if (code == 201) {
  33.                             //处理成功响应
  34.                             successNum++
  35.                             if (successNum == blockFileNum) {
  36.                                 XLogUtil.d("${TAG}upload all block file success,blockFileNum:$blockFileNum,,,requestId:$requestId")
  37.                                 listener?.apply {
  38.                                     onSuccess(Constant.SUCCESS, requestId)
  39.                                 }
  40.                                 //上传完
  41.                            
  42.                             }
  43.                         } else {
  44.                             //处理失败响应
  45.          
  46.                         }
  47.                     }
  48.                     override fun onFailure(call: Call<ResponseBody?>, t: Throwable) {
  49.                         // 处理请求失败
  50.                         XLogUtil.d("${TAG}upload 第${i + 1}块 block file onFailure message:${t.printStackTrace()}")
  51.       
  52.                     }
  53.                 })
  54.             }
  55.     /**
  56.      * 根据偏移量获取分块文件数据
  57.      */
  58.     fun getFileData(filePath: String, offset: Long, length: Int): ByteArray? {
  59.         // 使用RandomAccessFile随机访问文件
  60.         var randomAccessFile: RandomAccessFile? = null
  61.         try {
  62.             randomAccessFile = RandomAccessFile(filePath, "r")
  63.             val fileChannel = randomAccessFile.channel
  64.             // 将文件的部分区域映射为内存区域
  65.             val mappedByteBuffer =
  66.                 fileChannel.map(FileChannel.MapMode.READ_ONLY, offset, length.toLong())
  67.             val data = ByteArray(length)
  68.             // 从映射区域中读取数据
  69.             mappedByteBuffer[data]
  70.             return data
  71.         } catch (e: Exception) {
  72.             e.printStackTrace()
  73.             return null
  74.         } finally {
  75.             if (randomAccessFile != null) {
  76.                 try {
  77.                     randomAccessFile.close()
  78.                 } catch (e: Exception) {
  79.                     e.printStackTrace()
  80.                 }
  81.             }
  82.         }
  83.     }
复制代码

2.2、分段下载关键代码

  1.                 val folder = File(DOWNLOAD_FOLDER_PATH)
  2.                             if (!folder.exists()) {
  3.                                 folder.mkdirs()
  4.                             }
  5.                             val fileName = getFileNameWithPath(filePath)
  6.                             val downFile = File(DOWNLOAD_FOLDER_PATH + File.separator + fileName)
  7.                             if (!downFile.exists()) {
  8.                                 downFile.createNewFile()
  9.                             }
  10.                             // 使用输入流保存响应体到文件,这里通常是通过http请求,返回的文件流,替换即可
  11.                             val inputStream = body.byteStream()
  12.                             val rw = RandomAccessFile(downFile, "rw")
  13.                             rw.seek(startPosition)//文件写入的初始位置
  14.                             var hasReads = 0
  15.                             var readLenght: Long = 0
  16.                             val bytes = ByteArray(4096)
  17.                             while ((inputStream.read(bytes).also { hasReads = it }) > 0) {
  18.                                 rw.write(bytes, 0, hasReads)
  19.                                 readLenght += hasReads
  20.                                 // val l = (readLenght * 100 / contentLength) as Int 单块文件写入进度
  21.                             }
  22.                             // 关闭文件输出流和输入流
  23.                             inputStream.close()
  24.                             rw.close()
  25.     /**
  26.      * 根据文件路径获取文件名
  27.      */
  28.     fun getFileNameWithPath(path: String): String {
  29.         if (TextUtils.isEmpty(path)) {
  30.             return ""
  31.         }
  32.         val start = path.lastIndexOf("/")
  33.         return if (start != -1) {
  34.             path.substring(start + 1)
  35.         } else {
  36.             "DEFAULT_NAME"
  37.         }
  38.     }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

李优秀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表