主目录不允许下载媒体

问题描述 投票:0回答:3

尝试将 PDF 文件保存在下载目录中,但在 Android Q 后

getExternalStoragePublicDirectory
被完全弃用后,无法将文件保存在 DCIM 或 Pictures 文件夹之外的任何其他位置,因为在尝试保存文件时抛出以下异常在那里。

IllegalArgumentException:不允许主目录下载 内容://媒体/外部/图像/媒体;允许的目录是 [DCIM, 图片]

有以下代码。

private fun saveFile(input: ByteArray) {
    val fileName = "myFile.pdf"
    val outputStream = if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.Q) {
        val directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
        val file = File(directory, fileName)
        FileOutputStream(file)
    } else {
        val resolver = context.contentResolver
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
            put(MediaStore.MediaColumns.MIME_TYPE, "images/*")
            put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
        }
        resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)?.let {
            resolver.openOutputStream(it)
        }
    }
    outputStream?.use { stream ->
        stream.write(input)
    }
}

显然,当将路径更改为 DIRECTORY_DCIM 时,一切都会按预期工作,但由于要求,文件应像以前一样保存到下载中。 将不胜感激任何帮助。

android pdf save media android-10.0
3个回答
14
投票

未设置正确的文件保存 Uri,对于下载,应该是

resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)

blackapps感谢您的指点。


0
投票

当我需要的时候,我就这样做。

清单.xml

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" 
    android:maxSdkVersion="29" />
<application
  android:name=".BaseApp"
  android:requestLegacyExternalStorage="true"

IFileManager.kt

interface IFileManager {
  fun copyFile(sourceLocation: File, targetLocation: File)

  fun copyFileToDownload(filePath: String, fileName: String, fileType: String)
}

文件管理器.kt

class FileManager @Inject constructor(
    private val context: BaseApp
) : IFileManager {
    override fun copyFile(sourceLocation: File, targetLocation: File) {
        CoroutineScope(Dispatchers.IO).launch {
            runCatching {
                try {
                    val inSTR = FileInputStream(sourceLocation)
                    val out = FileOutputStream(targetLocation)
                    val buf = ByteArray(4096)
                    var len = 0
                    while (true) {
                        val read = inSTR.read(buf)
                        if (read == -1) {
                            break
                        }
                        out.write(buf, 0, read)
                        len += read
                    }
                    inSTR.close()
                    out.close()
                    Log.d("copyFile", "runCatching OK")
                } catch (e: Exception) {
                    Log.d("copyFile", "Exception ${e.message}")
                }
            }
        }
    }

    @RequiresApi(Build.VERSION_CODES.Q)
    override fun copyFileToDownload(filePath: String, fileName: String, fileType: String) {
       try {
           val contentResolver: ContentResolver = context.contentResolver
           val values = ContentValues()
           values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
           values.put(MediaStore.MediaColumns.MIME_TYPE, fileType)
           values.put(MediaStore.MediaColumns.TITLE, fileName)
           values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000)
           values.put(MediaStore.MediaColumns.DATE_TAKEN, System.currentTimeMillis())
           values.put(MediaStore.MediaColumns.IS_PENDING, 1)
           val mediaUri = contentResolver.insert(
               MediaStore.Downloads.EXTERNAL_CONTENT_URI,
               values
           )
           val bytes: ByteArray = Files.readAllBytes(Paths.get(filePath))
           contentResolver.openOutputStream(mediaUri!!).use { out ->
               out!!.write(bytes)
           }
           values.put(MediaStore.MediaColumns.IS_PENDING, 0)
           values.put(
               MediaStore.MediaColumns.RELATIVE_PATH,
               Environment.DIRECTORY_DOWNLOADS
           )
           contentResolver.update(mediaUri, values, null, null)
           Log.d("copyFileToDownload", "OK: ${values.get(MediaStore.MediaColumns.RELATIVE_PATH)}")
       }catch (e: Exception){
           Log.d("copyFileToDownload", "Exception: ${e.message}")
       }
    }

}

MainActivity.kt

@AndroidEntryPoint
open class MainActivity : ComponentActivity() {
  @Inject
  lateinit var iFileManager: IFileManager
  
   override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        savePdf(path = "your file path")
   }
   
    private fun savePdf(path: String){
      val result = path.split("/")
      val fileName = result[result.size - 1]
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
          iFileManager.copyFileToDownload(
            filePath = path,
            fileName = fileName,
            fileType = "application/pdf"
          )
      }else{
         val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS+"/$fileName")
          iFileManager.copyFile(
            sourceLocation = File(path),
            targetLocation = dir
          )
      }
    }
   
}

-2
投票

在 Android 11 上,应用程序无法再访问外部存储中任何其他应用程序的专用、特定于应用程序的目录中的文件。为了保护用户隐私,在运行 Android 11 或更高版本的设备上,系统进一步限制您的应用对其他应用私有目录的访问。

请求 MANAGE_EXTERNAL_STORAGE 权限

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
© www.soinside.com 2019 - 2024. All rights reserved.