Android-读/写到可移动SD卡 [英] Android - R/W to removable SD card

查看:75
本文介绍了Android-读/写到可移动SD卡的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am creating app, that should move files from internal storage (I mean /storage/emulated/0/*) to an external removable storage (micro SD card). I find the path to root of that SD card, but I am unable to create folder or move file there. I have no idea what to do to make it work. I found something called Storage Access Framework, but I did not understand, how to use it. I will appreciate any help to get read/write permission for specific SD card root path.

先谢谢您.

推荐答案

自api 19(KitKat)以来,几乎不可能直接以简单的File形式直接写入SDcard内容,因为至少对于默认用户,它们以只读方式安装(系统仍然可以对其进行写入).

Since api 19 (KitKat) it's almost impossible to write to SDcard content directly as a simple File because they are mounted as READ ONLY at least for default user (system can still write to it).

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

DocumentsProvider API is rather convoluted (even sample in docs is a hard read), that's why DocumentFile was added to simulate File behavior for easier access.

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

Assuming user already granted read/write storage permissions, we need to obtain document URI of SDcard root (in an 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的事实.

Before API 24 user needs to open document picker and manually select SD card themselves. This is not ideal but Android team overlooked the fact that SD card API is lacking.

在较新版本中 getStorageVolumes() 允许我们通过代码查找SDcard,并且仅向用户显示明确的警告,告知它将被访问.该对话框与读/写存储"权限不同.

In newer version getStorageVolumes() allows us to find SDcard through code, and only display explicit warning to the user that it will be accessed. This is NOT the same dialog as Read/Write storage permission.

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

Now to handle obtained Uri we only need to take data.data of the result. It might be good place to store it in shared preferences to prevent asking user over and over for it:

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卡根):

I will leave out most error checks/file exist validations, but now You can use obtained sdCardUri like this (copying file "sample.txt" from internal storage root to SDcard root):

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卡到内部存储):

And other way around (from SDcard to internal storage):

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()
}

这篇关于Android-读/写到可移动SD卡的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆