Android通过okhttp下载文件(本文案例 下载mp4到本地,并更新到相册)

news/2024/12/18 18:57:48/

使用步骤分为两步

okhttp3__1">第一步导入 okhttp3 依赖

第二步调用本文提供的 utils

第一步这里不做说明了,直接提供第二步复制即用

在这里插入图片描述

DownloadUtil 中 download 为下载文件 参数说明

在这里插入图片描述
这里主要看你把 destFileName 下载文件名称定义为什么后缀,比如我定义为 .mp4 下载后 就是 mp4 格式

这里 destFileDir 下载目录要说一下,如果没有开启存储权限或者使用了系统默认路径就会报错 比如 /0 文件一类的错误,怎么使用可以参考 open failed: ENOENT (No such file or directory) 解决办法

DownloadUtil 中 saveVideoToAlbum 为将下载好的视频更新到手机图库中,原来的放式已经随着安全性提高不适用了,这里基本就是复制出一份更新到系统层的文件夹

源码

DownloadUtil.download(mVideoUrl,getUrlPath(),"sing示例名称${System.currentTimeMillis()}.mp4",object : DownloadUtil.OnDownloadListener{override fun onDownloadSuccess(file: File?) {"下载成功".toast()//更新视频到相册DownloadUtil.saveVideoToAlbum(this@MoreActivity,file?.absolutePath)Log.e("视频下载", "下载成功: ${file?.absolutePath}")}override fun onDownloading(progress: Int) {Log.e("视频下载", "下载进度:${progress}")}override fun onDownloadFailed(e: Exception?) {LoadingSingDialog.dismiss()Log.e("视频下载", "下载失败:${e?.printStackTrace()}")}})

DownloadUtil

object DownloadUtil {private var okHttpClient: OkHttpClient? = null/*** @param url          下载连接* @param destFileDir  下载的文件储存目录* @param destFileName 下载文件名称* @param listener     下载监听*/fun download(url: String, destFileDir: String, destFileName: String, listener: OnDownloadListener) {if (url == null || url == ""){return}if (okHttpClient == null){okHttpClient = OkHttpClient()}val request: Request = Request.Builder().url(url).build()okHttpClient!!.newCall(request).enqueue(object : Callback {override fun onFailure(call: Call, e: IOException) {// 下载失败监听回调listener.onDownloadFailed(e)}@Throws(IOException::class)override fun onResponse(call: Call, response: Response) {if (response.body != null) {var inputStream: InputStream? = nullval buf = ByteArray(2048)var len = 0var fos: FileOutputStream? = null// 储存下载文件的目录val dir = File(destFileDir)if (!dir.exists()) {dir.mkdirs()}val file = File(dir, destFileName)try {inputStream = response.body!!.byteStream()val total: Long = response.body!!.contentLength()fos = FileOutputStream(file)var sum: Long = 0while (inputStream.read(buf).also { len = it } != -1) {fos.write(buf, 0, len)sum += len.toLong()val progress = (sum * 1.0f / total * 100).toInt()// 下载中更新进度条listener.onDownloading(progress)}fos.flush()// 下载完成listener.onDownloadSuccess(file)} catch (e: Exception) {listener.onDownloadFailed(e)} finally {try {inputStream?.close()} catch (e: IOException) {listener.onDownloadFailed(e)}try {fos?.close()} catch (e: IOException) {listener.onDownloadFailed(e)}}}else{listener.onDownloadFailed(IOException("接口失败"))}}})}interface OnDownloadListener {/*** @param file 下载成功后的文件*/fun onDownloadSuccess(file: File?)/*** @param progress 下载进度*/fun onDownloading(progress: Int)/*** @param e 下载异常信息*/fun onDownloadFailed(e: Exception?)}/*** 将视频保存到系统相册*/fun saveVideoToAlbum(context: Context, videoFile: String?): Boolean {if (videoFile == null || videoFile == ""){return false}return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {saveVideoToAlbumBeforeQ(context, videoFile)} else {saveVideoToAlbumAfterQ(context, videoFile)}}private fun saveVideoToAlbumAfterQ(context: Context, videoFile: String): Boolean {return try {val contentResolver = context.contentResolverval tempFile = File(videoFile)val contentValues = getVideoContentValues(context, tempFile, System.currentTimeMillis())val uri =contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)copyFileAfterQ(context, contentResolver, tempFile, uri)contentValues.clear()contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)context.contentResolver.update(uri!!, contentValues, null, null)context.sendBroadcast(Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri))true} catch (e: java.lang.Exception) {e.printStackTrace()false}}private fun saveVideoToAlbumBeforeQ(context: Context, videoFile: String): Boolean {val picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)val tempFile = File(videoFile)val destFile = File(picDir, context.packageName + File.separator + tempFile.name)var ins: FileInputStream? = nullvar ous: BufferedOutputStream? = nullreturn try {ins = FileInputStream(tempFile)ous = BufferedOutputStream(FileOutputStream(destFile))var nread = 0Lval buf = ByteArray(1024)var n: Intwhile (ins.read(buf).also { n = it } > 0) {ous.write(buf, 0, n)nread += n.toLong()}MediaScannerConnection.scanFile(context, arrayOf(destFile.absolutePath), arrayOf("video/*")) { path: String?, uri: Uri? -> }true} catch (e: java.lang.Exception) {e.printStackTrace()false} finally {try {ins?.close()ous?.close()} catch (e: IOException) {e.printStackTrace()}}}@Throws(IOException::class)private fun copyFileAfterQ(context: Context,localContentResolver: ContentResolver,tempFile: File,localUri: Uri?) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q &&context.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q) {//拷贝文件到相册的uri,android10及以上得这么干,否则不会显示。可以参考ScreenMediaRecorder的save方法val os = localContentResolver.openOutputStream(localUri!!)Files.copy(tempFile.toPath(), os)os!!.close()tempFile.delete()}}/*** 获取视频的contentValue*/private fun getVideoContentValues(context: Context, paramFile: File, timestamp: Long): ContentValues {val localContentValues = ContentValues()if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {localContentValues.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM+ File.separator + context.packageName)}localContentValues.put(MediaStore.Video.Media.TITLE, paramFile.name)localContentValues.put(MediaStore.Video.Media.DISPLAY_NAME, paramFile.name)localContentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4")localContentValues.put(MediaStore.Video.Media.DATE_TAKEN, timestamp)localContentValues.put(MediaStore.Video.Media.DATE_MODIFIED, timestamp)localContentValues.put(MediaStore.Video.Media.DATE_ADDED, timestamp)localContentValues.put(MediaStore.Video.Media.SIZE, paramFile.length())return localContentValues}}

http://www.ppmy.cn/news/1556183.html

相关文章

YOLO系列,从V1~V10,持续更新(Pytorch实现)

关于YOLOV1 本文主要偏向代码实战&#xff0c;详细的算法原理着墨不多 主要创新&#xff1a; 实现了实时的目标检测 附上YOLOV1论文地址&#xff1a; https://arxiv.org/abs/1506.02640 关于数据 首先要了解该网络架构的输入和输出&#xff0c;以及网络是如何进行训练的。这里…

workman服务端开发模式-应用开发-gateway的onWebSocketConnect开发

一、onWebSocketConnect安全开发一 为什么叫安全开发一&#xff0c;是因为还有两种情况没有考虑好&#xff0c;需要实测中进行修改。第一种情况&#xff1a;个人如果打开两个窗口&#xff0c;访问同一个系统或个人如果打开两种浏览器&#xff0c;两个窗口同时访问一个系统&…

gpu硬件架构

1.简介 NVIDIA在视觉计算和人工智能&#xff08;AI&#xff09;领域处于领先地位&#xff1b;其旗舰GPU已成为解决包括高性能计算和人工智能在内的各个领域复杂计算挑战所不可或缺的。虽然它们的规格经常被讨论&#xff0c;但很难掌握各种组件的清晰完整的图景。 这些GPU的高性…

多线程----互斥

以下是关于多线程中互斥相关的详细知识点介绍以及C语言代码示例&#xff1a; 一、互斥的概念与重要性 在多线程编程中&#xff0c;多个线程可能会同时访问和操作共享资源&#xff08;如全局变量、共享的数据结构等&#xff09;。如果没有适当的控制机制&#xff0c;就可能导致…

Java 系统稳定性进阶之路:策略、实践与优化

前言 最近在做系统的稳定性治理&#xff0c;有一点心得和感悟&#xff0c;分享、记录和输出一下&#xff0c;走几步要回头看看&#xff0c;沉淀一下。 稳定性建设-八股文 H-热点(Hotkey)R-限流(RateLimiter)A-授权(Authorize)B-隔离(BulkHead)C-熔断(CircuitBreaker)(服务熔…

RK3588平台上YOLOv8模型转换与CentOS 7.8 Docker镜像拉取超时问题解决指南

RK3588平台上YOLOv8模型转换与CentOS 7.8 Docker镜像拉取超时问题解决指南 一、RK3588平台上YOLOv8模型从PT转换为RKNN 背景介绍 YOLOv8的原生模型包含了后处理步骤&#xff0c;其中一些形状超出了RK3588的矩阵计算限制&#xff0c;因此需要对输出层进行一些裁剪。本文将详细…

SpringCloud--SpringCloudAlibaba 对应的版本选择

SpringCloud--SpringCloudAlibaba 对应的版本选择 1、进入官网查看 输入Spring Cloud Alibaba官网_基于Springboot的微服务教程-阿里云 网址&#xff0c;选择文档->点击版本&#xff08;2023.x&#xff09; ​ 2、选择版本发布说明&#xff0c;可以看到Spring Cloud Ali…

禁用硬件合成 (Hardware Composer, HWC)

要禁用硬件合成 (Hardware Composer, HWC)&#xff0c;通常需要根据具体的设备平台和系统环境选择适合的方法。以下是通用的解决方案&#xff1a; 1. 修改系统属性 在 Android 系统中&#xff0c;可以通过设置系统属性来禁用 HWC 合成&#xff1a; a. 使用 setprop 命令临时禁…