最近测试把文件写入到系统Download目录时发现直接把文件写入到Download中时没有问题。但是如果在Download中添加了一级目录,比如Download/Test时,第一次安装app,而且download目录下没有Test时,可以正常写入文件。但是卸载重装app或者Download中原来就有Test目录,写入文件时就会出现以下异常
文件写入代码如下:
private fun createFile1(){
val path = "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/Test"
val directory = File(path)
if(!directory.exists()){
directory.mkdirs()
}
val file = File(directory,"test.txt")
FileOutputStream(file).use { outputStream->outputStream.write("Hello world!".toByteArray()) }
}
解决方式用三种,第一种是手动删除掉Download中的Test目录,或者创建子目录时确保子目录在Download中不存在,这种解决方法简单粗暴。
第二种就是申请所有文件管理权限。
第三种就是用MediaStore写入文件,代码如下:
MediaStore 写入文件
/**
* 创建文件到download目录
*/
private fun createFile() {
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "sdf.txt")// 文件名
put(MediaStore.MediaColumns.MIME_TYPE, "text/plain")// 文件类型
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/Test1")// 文件保存相对路径
}
val uri = contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
uri?.let {
contentResolver.openOutputStream(uri).use { outputStream ->
outputStream?.write("hello world".toByteArray())
} ?: run {
Log.e("uri", "Failed to create file")
}
}
}
除了Download目录,其他的如Pictures,DCIM也是类似
/**
* 创建图片到Picture目录
*/
private fun createImage() {
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME, "test")
put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Test1")
// 图片专用参数
put(MediaStore.Images.ImageColumns.DESCRIPTION,"描述测试")
// put(MediaStore.Images.ImageColumns.DATE_TAKEN,Date().timeStamp())
}
val uri: Uri? = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
val bitmap = AppCompatResources.getDrawable(this,R.drawable.img1)?.toBitmap()
uri?.let {
contentResolver.openOutputStream(it).use {outputStream->
bitmap?.compress(Bitmap.CompressFormat.PNG,90,outputStream!!)
}
}
}
private fun createDCIM(){
val values = ContentValues().apply {
put(MediaStore.MediaColumns.DISPLAY_NAME,"照片名")
put(MediaStore.MediaColumns.MIME_TYPE,"image/png")
put(MediaStore.MediaColumns.RELATIVE_PATH,Environment.DIRECTORY_DCIM+"/Test")
}
val uri:Uri? = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,values)
val bitmap = AppCompatResources.getDrawable(this,R.drawable.img1)?.toBitmap()
uri?.let {
contentResolver.openOutputStream(it).use {outputStream->
bitmap?.compress(Bitmap.CompressFormat.PNG,90,outputStream!!)
}
}
说完了写入,接下来看一下查询文件
MediaStore查询文件
private fun queryFiles(context: Context): List<Uri> {
val uriList = mutableListOf<Uri>()
sb.setLength(0)
val projection = arrayOf(MediaStore.Downloads._ID, MediaStore.Downloads.DISPLAY_NAME)
val uri: Uri = MediaStore.Downloads.EXTERNAL_CONTENT_URI
context.contentResolver.query(uri, projection, null, null, null)?.use { cursor ->
Log.e("查询结果数量", cursor.count.toString())
while (cursor.moveToNext()) {
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
val contentUri: Uri = ContentUris.withAppendedId(
uri,
cursor.getLong(idColumn)
)
uriList.add(contentUri)
sb.append(cursor.getString(nameColumn) + "\n")
// 打印图片名称和URI
Log.e("名称", cursor.getString(nameColumn))
Log.e("URI", contentUri.toString())
}
}
binding.text.text = sb.toString()
return uriList
}
query方法里面的参数和SQLiteHelper查询的参数基本上是一致的,区别在于这个查询第一个参数不是传的数据库名,而是Uri,其他的参数用法和SQLiteHelper数据库一模一样。
projection:用于指定需要从查询中返回哪些列。如果返回所有列,可以设置为 null
。
selection:指定查询条件,比如"${MediaStore.Images.Media.DISPLAY_NAME} LIKE ?",查询图片名中包含“?”的图片,其中问号为占位符,通过下一个参数selectionArgs传入。
selectionArgs:与 selection
参数一起使用,用于为 selection
字符串中的占位符提供具体的值,防止SQL注入。
如果除Uri外的参数都传Null,就表示不进行任何筛选,并且查询结果中会返回所有列。
筛选图片名中包含指定名字的图片:
private fun queryImageByName(name: String): List<Bitmap> {
val result = mutableListOf<Bitmap>()
val uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
// 正确使用LIKE关键字的通配符%
val selection = "${MediaStore.Images.Media.DISPLAY_NAME} LIKE ?"
val selectionArgs = arrayOf("%$name%")
// 查询
contentResolver.query(uri, null, selection, selectionArgs, null)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)
// 遍历所有匹配项
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val imageUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
val bitmap = contentResolver.openInputStream(imageUri)?.use { inputStream ->
BitmapFactory.decodeStream(inputStream)
}
bitmap?.let {
result.add(it)
}
}
}
Log.e("图片数量", result.size.toString())
return result
}