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

news2024/11/24 16:48:49

一、背景

Android 中的大文件下载需要使用分段下载,下载通常是在线程中进行的,假如有5段,那同时5个线程去执行下载,请求http返回文件流后,需要将多个文件流同时写进同一个文件,这里用到

RandomAccessFile。

分段上传的话,只需要根据每段文件阀值,例如,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"
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2246764.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HarmonyOS:使用ArkWeb构建页面

一、简介 页面加载是Web组件的基本功能。根据页面加载数据来源可以分为三种常用场景&#xff0c;包括加载网络页面、加载本地页面、加载HTML格式的富文本数据。 页面加载过程中&#xff0c;若涉及网络资源获取&#xff0c;需要配置ohos.permission.INTERNET网络访问权限。 二、…

矩阵的拼接

矩阵的拼接分为横向拼接和纵向拼接 注意&#xff1a;横向拼接要求两矩阵行数相同&#xff0c;纵向拼接要求两矩阵列数相同 h o r z c a t horzcat horzcat和 v e r t c a t vertcat vertcat函数 h o r z c a t ( a , b ) horzcat(a,b) horzcat(a,b)将 a a a和 b b b横向拼接&a…

SpringCloud框架学习(第五部分:SpringCloud Alibaba入门和 nacos)

目录 十二、SpringCloud Alibaba入门简介 1. 基本介绍 2.作用 3.版本选型 十三、 SpringCloud Alibaba Nacos服务注册和配置中心 1.简介 2.各种注册中心比较 3.下载安装 4.Nacos Discovery服务注册中心 &#xff08;1&#xff09; 基于 Nacos 的服务提供者 &#xf…

Ollama vs VLLM:大模型推理性能全面测评!

最近在用本地大模型跑实验&#xff0c;一开始选择了ollama,分别部署了Qwen2.5-14B和Qwen2.5-32B&#xff0c;发现最后跑出来的实验效果很差&#xff0c;一开始一直以为prompt的问题&#xff0c;尝试了不同的prompt&#xff0c;最后效果还是一直不好。随后尝试了vllm部署Qwen2.5…

.NET9 - 新功能体验(一)

被微软形容为“迄今为止最高效、最现代、最安全、最智能、性能最高的.NET版本”——.NET 9已经发布有一周了&#xff0c;今天想和大家一起体验一下新功能。 此次.NET 9在性能、安全性和功能等方面进行了大量改进&#xff0c;包含了数千项的修改&#xff0c;今天主要和大家一起体…

LeetCode 144.二叉树的前序遍历

题目&#xff1a;给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 思路&#xff1a;根 左 右 代码&#xff1a; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNod…

【论文阅读】WGSR

0. 摘要 0.1. 问题提出 1.超分辨率(SR)是一个不适定逆问题&#xff0c;可行解众多。 2.超分辨率(SR)算法在可行解中寻找一个在保真度和感知质量之间取得平衡的“良好”解。 3.现有的方法重建高频细节时会产生伪影和幻觉&#xff0c;模型区分图像细节与伪影仍是难题。 0.2. …

游戏引擎学习第21天

虽然没有上一节的难但是内容也很多 关于实现和使用脚本语言 以下是详细复述&#xff1a; 许多人经常问一个问题&#xff0c;反复问过好几次&#xff0c;那就是&#xff1a;是否会在项目中实现脚本语言。这个问题的具体形式通常是&#xff1a;你们会使用脚本语言吗&#xff1…

NVR接入录像回放平台EasyCVR视频融合平台加油站监控应用场景与实际功能

在现代社会中&#xff0c;加油站作为重要的能源供应点&#xff0c;面临着安全监管与风险管理的双重挑战。为应对这些问题&#xff0c;安防监控平台EasyCVR推出了一套全面的加油站监控方案。该方案结合了智能分析网关V4的先进识别技术和EasyCVR视频监控平台的强大监控功能&#…

springboot vue工资管理系统源码和答辩PPT论文

人类现已迈入二十一世纪&#xff0c;科学技术日新月异&#xff0c;经济、资讯等各方面都有了非常大的进步&#xff0c;尤其是资讯与网络技术的飞速发展&#xff0c;对政治、经济、军事、文化等各方面都有了极大的影响。 利用电脑网络的这些便利&#xff0c;发展一套工资管理系统…

部署实战(二)--修改jar中的文件并重新打包成jar文件

一.jar文件 JAR 文件就是 Java Archive &#xff08; Java 档案文件&#xff09;&#xff0c;它是 Java 的一种文档格式JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中&#xff0c;多出了一个META-INF/MANIFEST.MF 文件META-INF/MANIFEST.MF 文件在生成 JAR 文件的时候…

RabbitMQ4:work模型

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…

SpringMVC——简介及入门

SpringMVC简介 看到SpringMVC这个名字&#xff0c;我们会发现其中包含Spring&#xff0c;那么SpringMVC和Spring之间有怎样的关系呢&#xff1f; SpringMVC隶属于Spring&#xff0c;是Spring技术中的一部分。 那么SpringMVC是用来做什么的呢&#xff1f; 回想web阶段&#x…

鸿蒙开发-文件与分享

文件分类 按所有者&#xff1a; 应用文件&#xff1a;所有者为应用&#xff0c;包括应用安装文件、应用资源文件、应用缓存文件等。 用户文件&#xff1a;所有者是登录到该终端设备的用户&#xff0c;包括用户私有的图片、视频、音频、文档等。 系统文件&#xff1a;与应用和…

内存级文件原理——Linux

目录 进程与文件 Linux下的文件系统 文件操作&#xff0c;及文件流 C语言函数 文件流 文件描述符 系统调用操作 系统调用参数 重定向与文件描述符 输出重定向 输入重定向 文件内容属性 Linux下一切皆文件 进程与文件 当我们对文件进行操作时&#xff0c;文件必…

KubeSphere 最佳实战:K8s 构建高可用、高性能 Redis 集群实战指南

首发&#xff1a;运维有术。 本指南将逐步引导您完成以下关键任务&#xff1a; 安装 Redis&#xff1a;使用 StatefulSet 部署 Redis。自动或手动配置 Redis 集群&#xff1a;使用命令行工具初始化 Redis 集群。Redis 性能测试&#xff1a;使用 Redis 自带的 Benchmark 工具进…

apr共享内存

下载&#xff1a; Download - The Apache Portable Runtime Project 编译&#xff1a; 使用cmake-gui生成库&#xff1a; apr-1.lib aprapp-1.lib libapr-1.lib libaprapp-1.lib libapr-1.dll 在Developer PowerShell for VS 2019中&#xff1a; 执行nmake -f Makefile.win来…

Javaweb前端HTML css 整体布局

最后一个是线条颜色 盒子&#xff0c;整体还是300&#xff0c;400

5.STM32之通信接口《精讲》之USART通信---实验串口接收程序

根据上节&#xff0c;我们一已经完成了串口发送程序的代码&#xff0c;并且深入的解析探索了串口的原理&#xff0c;接下来&#xff0c;Whappy小编将带领大家进入串口接收程序的探索与实验&#xff0c;并将结合上一节串口发送一起来完成串口的发送和接收实验。 上来两张图 上图…

借助算力云跑模型

算力平台&#xff1a;FunHPC | 算力简单易用 AI乐趣丛生 该文章只讲述了最基本的使用步骤&#xff08;因为我也不熟练&#xff09;。 【注】&#xff1a;进入平台&#xff0c;注册登录账号后&#xff0c;才能租用。学生认证&#xff0b;实名认证会有免费的算力资源&#xff0…