使用步骤分为两步
第一步导入 okhttp3 依赖
第二步调用本文提供的 utils
第一步这里不做说明了,直接提供第二步复制即用
DownloadUtil 中 download 为下载文件 参数说明
这里主要看你把 destFileName 下载文件名称定义为什么后缀,比如我定义为 .mp4 下载后 就是 mp4 格式
这里 destFileDir 下载目录要说一下,如果没有开启存储权限或者使用了系统默认路径就会报错 比如 /0 文件一类的错误,怎么使用可以参考 open failed: ENOENT (No such file or directory) 解决办法
DownloadUtil 中 saveVideoToAlbum 为将下载好的视频更新到手机图库中,原来的放式已经随着安全性提高不适用了,这里基本就是复制出一份更新到系统层的文件夹
源码
java
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
java
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? = null
val buf = ByteArray(2048)
var len = 0
var 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 = 0
while (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.contentResolver
val 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? = null
var ous: BufferedOutputStream? = null
return try {
ins = FileInputStream(tempFile)
ous = BufferedOutputStream(FileOutputStream(destFile))
var nread = 0L
val buf = ByteArray(1024)
var n: Int
while (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
}
}