Android-读/写到可移动SD卡

时间:2019-03-03 19:38:45

标签: android kotlin

我正在创建应用,该应用应将文件从内部存储(我的意思是/ storage / emulated / 0 / *)移动到外部可移动存储(微型SD卡)。我找到了该SD卡根目录的路径,但是无法在其中创建文件夹或移动文件。我不知道该怎么做才能使其正常工作。 我找到了一个称为“存储访问框架”的东西,但是我不明白如何使用它。 对于获得特定SD卡根路径的读/写权限的任何帮助,我们将不胜感激。

先谢谢您。

1 个答案:

答案 0 :(得分:1)

自api 19(KitKat)起,几乎不可能像简单的File一样直接写入SD卡内容,因为至少对于默认用户,它们以只读方式装载(系统仍然可以对其进行写入)。

DocumentsProvider API相当复杂(即使文档中的示例也很难读),这就是为什么添加DocumentFile来模拟File行为以便于访问。

假设用户已被授予读/写存储权限,我们需要获取SD卡根目录的文档URI(在Activity中):

var sdCardUri : Uri? = null

private fun requestSDCardPermissions(){
    if(Build.VERSION.SDK_INT < 24){
        startActivityForResult(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE), REQ_PICK_DIRECTORY)
        return
    }
    // find removable device using getStorageVolumes
    val sm = getSystemService(Context.STORAGE_SERVICE) as StorageManager
    val sdCard = sm.storageVolumes.find { it.isRemovable }
    if(sdCard != null){
        startActivityForResult(sdCard.createAccessIntent(null), REQ_SD_CARD_ACCESS)
    }
}

在使用API​​ 24之前,用户需要打开文档选择器并自己手动选择SD卡。这不是很理想,但是Android团队忽略了缺少SD卡API的事实。

在较新的版本getStorageVolumes()中,我们可以通过代码查找SD卡,并且仅向用户显示明确的警告,即将其访问。该对话框与“读取/写入存储”权限不同。

现在要处理获得的Uri,我们只需要取data.data的结果。最好将它存储在共享的首选项中,以防止一遍又一遍地询问用户:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if(requestCode == REQ_SD_CARD_ACCESS || requestCode == REQ_PICK_DIRECTORY){
        if(resultCode == RESULT_OK) {
            if(data == null){
                Log.e(TAG, "Error obtaining access")
            }else{
                sdCardUri = data.data
                Log.d("StorageAccess", "obtained access to $sdCardUri")
                // optionally store uri in preferences as well here { ... }
            }
        }else
            Toast.makeText(this, "access denied", Toast.LENGTH_SHORT).show()
        return
    }
    super.onActivityResult(requestCode, resultCode, data)
}

我将省去大多数错误检查/文件存在的验证,但是现在您可以像这样使用获得的sdCardUri(将文件“ sample.txt”从内部存储根复制到SD卡根):

private fun copyToSDCard(){
    val sdCardRoot = DocumentFile.fromTreeUri(this, sdCardUri)
    val internalFile = File(Environment.getExternalStorageDirectory(), "sample.txt")
    // get or create file
    val sdCardFile = sdCardRoot.findFile("sample.txt") ?: sdCardRoot.createFile(null, "sample.txt")
    val outStream = contentResolver.openOutputStream(sdCardFile.uri)
    outStream.write(internalFile.readBytes())
    outStream.flush()
    outStream.close()
    Toast.makeText(this, "copied to SDCard", Toast.LENGTH_SHORT).show()
}

以其他方式(从SD卡到内部存储):

private fun copyToInternal(){
    val sdCardRoot = DocumentFile.fromTreeUri(this, sdCardUri)
    val internalFile = File(Environment.getExternalStorageDirectory(), "sample.txt")
    val sdCardFile = sdCardRoot.findFile("sample.txt")
    val inStream = contentResolver.openInputStream(sdCardFile.uri)
    internalFile.writeBytes(inStream.readBytes())
    inStream.close()
    Toast.makeText(this, "copied to internal", Toast.LENGTH_SHORT).show()
}
相关问题