Files
taimed-international-app/uni_modules/uni-chooseSystemImage/utssdk/app-android/FileUtils.kt

210 lines
8.3 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package uts.sdk.modules.uniChooseSystemImage
import android.content.ContentResolver
import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.webkit.MimeTypeMap
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.net.URLConnection
import java.security.MessageDigest
object FileUtils {
fun getFilePathByUri(context: Context, uri: Uri): String? {
var path: String? = null
// 以 file:// 开头的
if (ContentResolver.SCHEME_FILE == uri.scheme) {
path = uri.path
return path
}
// 以 content:// 开头的,比如 content://media/extenral/images/media/17766
if (ContentResolver.SCHEME_CONTENT == uri.scheme && Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
val cursor = context.contentResolver.query(
uri,
arrayOf(MediaStore.Images.Media.DATA),
null,
null,
null
)
if (cursor != null) {
if (cursor.moveToFirst()) {
val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA)
if (columnIndex > -1) {
path = cursor.getString(columnIndex)
}
}
cursor.close()
}
return path
}
// 4.4及之后的 是以 content:// 开头的,比如 content://com.android.providers.media.documents/document/image%3A235700
if (ContentResolver.SCHEME_CONTENT == uri.scheme && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (DocumentsContract.isDocumentUri(context, uri)) {
if (isExternalStorageDocument(uri)) {
// ExternalStorageProvider
val docId = DocumentsContract.getDocumentId(uri)
val split =
docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
if ("primary".equals(type, ignoreCase = true)) {
path = Environment.getExternalStorageDirectory().toString() + "/" + split[1]
return path
}
} else if (isDownloadsDocument(uri)) {
// DownloadsProvider
val id = DocumentsContract.getDocumentId(uri)
val contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"),
id.toLong()
)
path = getDataColumn(context, contentUri, null, null)
return path
} else if (isMediaDocument(uri)) {
// MediaProvider
val docId = DocumentsContract.getDocumentId(uri)
val split =
docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val type = split[0]
var contentUri: Uri? = null
if ("image" == type) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
} else if ("video" == type) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
} else if ("audio" == type) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
}
val selection = "_id=?"
val selectionArgs = arrayOf(split[1])
path = getDataColumn(context, contentUri, selection, selectionArgs)
return path
}
}
}
return null
}
// 新增:将 uri 拷贝到传入的父文件夹,文件名为 uri 的 MD5后缀从头信息或原文件名推断。
// 若目标文件已存在则直接返回已存在路径,不重复拷贝。
// 返回目标文件的绝对路径,失败返回 null。
fun copyUriToDir(context: Context, parentDirStr: String, uriString: String): String? {
try {
var uri = Uri.parse(uriString)
var parentDir = File(parentDirStr)
val resolver = context.contentResolver
// 读取全部数据到内存(用于判断 MIME 并写入目标文件)
val inputStream = resolver.openInputStream(uri) ?: return null
val baos = ByteArrayOutputStream()
inputStream.use { ins ->
val buf = ByteArray(8 * 1024)
var len: Int
while (ins.read(buf).also { len = it } != -1) {
baos.write(buf, 0, len)
}
}
val data = baos.toByteArray()
// 通过头信息猜 MIME
var mime: String? = null
try {
mime = URLConnection.guessContentTypeFromStream(ByteArrayInputStream(data))
} catch (_: Exception) {
}
if (mime == null) {
try {
mime = resolver.getType(uri)
} catch (_: Exception) {
}
}
// 若仍为空,尝试从原始路径推断
var originalPath: String? = null
try {
originalPath = getFilePathByUri(context, uri)
} catch (_: Exception) {
}
// 根据 mime 获取扩展名
var ext: String? = null
if (mime != null) {
ext = MimeTypeMap.getSingleton().getExtensionFromMimeType(mime)
}
// 如果通过 mime 无法得到扩展名,尝试从原始路径取后缀
if (ext.isNullOrEmpty() && !originalPath.isNullOrEmpty()) {
val idx = originalPath.lastIndexOf('.')
if (idx != -1 && idx + 1 < originalPath.length) {
ext = originalPath.substring(idx + 1).lowercase()
}
}
val extSuffix = if (!ext.isNullOrEmpty()) ".${ext}" else ""
// 计算 MD5 作为文件名(基于 uri.toString()
val name = md5(uri.toString()) + extSuffix
// 确保父目录存在
if (!parentDir.exists()) {
parentDir.mkdirs()
}
val destFile = File(parentDir, name)
// 若已存在,直接返回
if (destFile.exists()) {
return destFile.absolutePath
}
// 写入文件
FileOutputStream(destFile).use { fos ->
fos.write(data)
fos.flush()
}
return destFile.absolutePath
} catch (e: Exception) {
// 出错返回 null
return null
}
}
// 辅助:计算字符串的 MD5小写 hex
private fun md5(input: String): String {
val md = MessageDigest.getInstance("MD5")
val bytes = md.digest(input.toByteArray(Charsets.UTF_8))
return bytes.joinToString("") { "%02x".format(it) }
}
private fun getDataColumn(
context: Context,
uri: Uri?,
selection: String?,
selectionArgs: Array<String>?,
): String? {
var cursor: Cursor? = null
val column = "_data"
val projection = arrayOf(column)
try {
cursor =
context.contentResolver.query(uri!!, projection, selection, selectionArgs, null)
if (cursor != null && cursor.moveToFirst()) {
val column_index = cursor.getColumnIndexOrThrow(column)
return cursor.getString(column_index)
}
} finally {
cursor?.close()
}
return null
}
private fun isExternalStorageDocument(uri: Uri): Boolean {
return "com.android.externalstorage.documents" == uri.authority
}
private fun isDownloadsDocument(uri: Uri): Boolean {
return "com.android.providers.downloads.documents" == uri.authority
}
private fun isMediaDocument(uri: Uri): Boolean {
return "com.android.providers.media.documents" == uri.authority
}
}