【问题标题】:Android write to /sdcard/Android 写入 /sdcard/
【发布时间】:2018-11-17 06:51:10
【问题描述】:

我需要将图像保存到 /sdcard/sample/0/b/image.png。我试过这个:

val dir = File("/sdcard/sample/0/b/")
dir.mkdirs()

val image = File(dir, "image.png")
image.createNewFile()

var out: FileOutputStream? = null
try {
    out = FileOutputStream(image)
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out)
} finally {
    out?.close()
}

而且我总是收到该文件不存在。另外,我尝试用 Environment.getExternalStorageDirectory().path 替换“/sdcard”,但结果是一样的。

java.io.IOException: No such file or directory
    at java.io.UnixFileSystem.createFileExclusively0(Native Method)
    at java.io.UnixFileSystem.createFileExclusively(UnixFileSystem.java:281)
    at java.io.File.createNewFile(File.java:1000)

在清单中,为了测试,我添加了

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<uses-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission-sdk-23 android:name="android.permission.READ_EXTERNAL_STORAGE"/>

【问题讨论】:

标签: android kotlin android-bitmap fileoutputstream


【解决方案1】:

我应该使用

val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE)
requestPermissions(permissions, 0x512)

即使我添加了 manifest sdk-23 的权限

<uses-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

【讨论】:

    【解决方案2】:

    尝试使用:

    val dir = File(Environment.getExternalStorageDirectory().absolutePath + "/b/")
    

    如果您没有 SDCard,这将返回“/storage/emulated/0”或类似内容,具体取决于手机型号和 Android 版本。 同时请求许可。

    【讨论】:

    • 技术规范要求写入 /sdcard/
    【解决方案3】:

    这个答案会很长,但它可以工作,并且所有步骤都包含在 BONUS CODE 中,该代码显示了如何优雅地管理权限。哦,要谦虚

    首先这段代码是用 Kotlin 编写的
    为清楚起见,您需要将其包含在您的清单文件夹中

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    我们将使用在模型数据类中声明的几个全局变量

    companion object { var THE_PATH = "";var gv = 3 }
    

    我们的 LAUNCHER Activity 被命名为 CheckStorageActivity 这里是长代码
    此活动检查用户是否有 SD 卡,然后根据检查结果向用户显示内部或外部存储对话框
    在任何这种情况发生之前,尽管用户需要管理授予权限
    注意事项在真实设备上测试此代码,查看代码中的 cmets

    class CheckStorageActivity : AppCompatActivity() {
    
    private val STORAGE_PERMISSION_CODE = 1
    var STORAGE_LOCATION: Int = 3
    var the_path = ""
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_check_storage)
        supportActionBar?.hide()
    
        val preferences = getSharedPreferences("FirstPreference", Context.MODE_PRIVATE)
    
        if (!preferences.getBoolean("isFirstTime", false)) {
    
            val sharedPreferences = getSharedPreferences("SecondPref", Context.MODE_PRIVATE)
            val editorshared = sharedPreferences.edit()
            editorshared.putString("DB_PATH", THE_PATH)
            editorshared.apply()
    
            val pref = getSharedPreferences("FirstPreference", Context.MODE_PRIVATE)
            val editor = pref.edit()
            editor.putBoolean("isFirstTime", true)
            editor.apply()
        }
    
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                // this only runs on re START APP 1
                val sharedPreferences = getSharedPreferences("SecondPref", Context.MODE_PRIVATE)
                the_path = sharedPreferences.getString("DB_PATH", "")
    
                if(the_path != ""){
                    pathONLY()
                }
                //showIMAGE()?
            }
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    
                requestStoragePermission()
            }
    
    }// end onCreate
    
    fun pathALL(){
    
        val sharedPreferences = getSharedPreferences("SecondPref", Context.MODE_PRIVATE)
        the_path = sharedPreferences.getString("DB_PATH", "")
    
        val fi = File("storage/")
        val lst = fi.listFiles()
        val top = lst[1].toString()
        val bot = lst[0].toString()
    
        println("TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT top $top")
        // TOP VALUE ON EMULATOR storage/self
        // THIS MUST BE USED ON emulator to test
        // For real device use storage/emulated for top String variable
        // =============================================================
        println("BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB bot $bot")
        // BOT VALUE ON EMULATOR storage/emulated
        // Do not use on Emulator it will NOT work
    
        if (top.contains("storage/self")) {
            STORAGE_LOCATION = 0
            internalDIALOG()
        }
        if (bot.contains("-")) {
            STORAGE_LOCATION = 1
            externalDIALOG()
        }
    }
    
    fun pathONLY(){
    
        val sharedPreferences = getSharedPreferences("SecondPref", Context.MODE_PRIVATE)
        the_path = sharedPreferences.getString("DB_PATH", "")
        THE_PATH = the_path
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }
    
    fun writeSharedPrefANDleave(){
    
        val sharedPreferences = getSharedPreferences("SecondPref", Context.MODE_PRIVATE)
        val editor = sharedPreferences.edit()
        editor.putString("DB_PATH", THE_PATH)
        editor.apply()
    
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }
    
    fun internalDIALOG() {
    
        val builder = AlertDialog.Builder(this)
        builder.setCancelable(false)
    
        builder.setTitle("SD Card NOT Mounted")
        builder.setMessage("\nClick OK to use INTERNAL Device Storage")
        builder.setNeutralButton("OK") { dialogInterface, i ->
            STORAGE_LOCATION == 0
            setThePath()
    
            writeSharedPrefANDleave()
        }
        builder.show()
    }
    
    fun externalDIALOG() {
    
        val builder = AlertDialog.Builder(this)
        builder.setCancelable(false)
    
        builder.setTitle("Select Data Storage Location ")
        builder.setMessage("EXTERNAL Use SD CARD\n\n" + "INTERNAL Device Storage")
    
        builder.setPositiveButton("EXTERNAL") { dialogInterface, i ->
            STORAGE_LOCATION = 1
            setThePath()
    
            writeSharedPrefANDleave()
        }
        builder.setNegativeButton("INTERNAL") { dialogInterface, i ->
            STORAGE_LOCATION = 0
            setThePath()
    
            writeSharedPrefANDleave()
        }
        builder.show()
    }
    
    fun setThePath(): String {
    
        val removable = ContextCompat.getExternalFilesDirs(this, null)[STORAGE_LOCATION]
        if (STORAGE_LOCATION == 1) {
            THE_PATH = removable.toString()
            THE_PATH = THE_PATH + "/Documents/"
        }
        if (STORAGE_LOCATION == 0) {
            THE_PATH = removable.toString()
            THE_PATH = THE_PATH + "/INTERNAL/"
        }
        return THE_PATH.trim()
    }
    
    companion object {var MANUALLY_SET = 1}
    // Manage Navigation if Permission Manually Set
    override fun onResume() {
        super.onResume()
        if(MANUALLY_SET == 2){
            pathALL()
        }
    }
    
    private fun requestStoragePermission() {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
    
            AlertDialog.Builder(this)
                    .setTitle("To Write to DB Allow Permissions")
                    .setMessage("On the Next Screen to Remove the App\n\nCheck Don't Ask Again and Click DENY\n\nOr Click ALLOW to Grant Permission")
                    .setPositiveButton("NEXT") { dialog, id ->
                        ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
                    }
                    .create().show()
        } else {
            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), STORAGE_PERMISSION_CODE)
        }
    }
    
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
        if (requestCode == STORAGE_PERMISSION_CODE) {
    
            if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                if(gv == 3){
                    pathALL()
                }
                //showIMAGE()
    
            } else {
                if (Build.VERSION.SDK_INT >= 23 && !shouldShowRequestPermissionRationale(permissions[0])) {
    
                    val alertDialogBuilder = AlertDialog.Builder(this)
                    alertDialogBuilder.setTitle("Set Permissions or UN-Install App")
                    alertDialogBuilder
                            .setMessage("Click SETTINGS to Manually Set\n\n"+"OR to UN-Install the Application")
                            .setCancelable(false)
                            .setPositiveButton("SETTINGS") { dialog, id ->
                                MANUALLY_SET = 2
                                val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
                                val uri = Uri.fromParts("package", packageName, null)
                                intent.data = uri
                                startActivityForResult(intent, 1000)
                            }
    
                    val alertDialog = alertDialogBuilder.create()
                    alertDialog.show()
    
                } else run {
                    // User selected Deny Dialog to EXIT App ==> OR <== RETRY to have a second chance to Allow Permissions
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
    
                        val alertDialogBuilder = AlertDialog.Builder(this)
                        alertDialogBuilder.setTitle("RETRY")
                        alertDialogBuilder
                                .setMessage("Click RETRY to Set Permissions\n\n"+"Permissions MUST be set to Enter Data")
                                .setCancelable(false)
                                .setPositiveButton("RETRY") { dialog, id ->
                                    dialog.cancel()
                                    val intent = Intent(this, CheckStorageActivity::class.java)
                                    startActivity(intent)
                                }
                        val alertDialog = alertDialogBuilder.create()
                        alertDialog.show()
                    }
                }
            }
        }
    }
    

    }

    现在在您的 DBHelper 类中,您可以在其中执行所有 CRUD 并使用此格式创建数据库

    import com.androidstackoverflow.kotlinsqlite.Note.Companion.THE_PATH
    

    class NoteDbManager(context: Context) {
    
    private val dbName = THE_PATH +"JSANotes.db"
    private val dbTable = "Notes"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-31
      • 2011-06-16
      • 1970-01-01
      相关资源
      最近更新 更多