如何修复 Kotlin 中 FileInputStream 和 FileDescriptor 的错误路径

Posted

技术标签:

【中文标题】如何修复 Kotlin 中 FileInputStream 和 FileDescriptor 的错误路径【英文标题】:How to fix wrong path for FileInputStream and FileDescriptor in kotlin 【发布时间】:2021-05-29 10:02:24 【问题描述】:

FILE 是使用 cacheDir 中的 URI 创建的,但是当我尝试获取路径时,找不到图像,在创建文件之前记录了 URI,并且能够看到图像文件的正确 URI。现在我在应用程序缓存中创建了一个文件并尝试检索图像的路径然后没有获得完整的图像路径,不确定图像是否创建这是我的代码

 val imagesList = data?.extras?.getStringArray(GligarPicker.IMAGES_RESULT)
                if (!imagesList.isNullOrEmpty()) 
                    val arrayList = ArrayList<MultipartBody.Part>()
                    for (i in 0 until imagesList.size) 
                        Log.e("imagesList.item", imagesList[i])
                        val uri = Uri.parse("file://" + imagesList[i].toString())
                        Log.e("URI", uri.toString())
                        val parcelFileDescriptor: ParcelFileDescriptor? =
                            requireContext().contentResolver.openFileDescriptor(uri, "r")
                        val fileDescriptor: FileDescriptor? = parcelFileDescriptor?.fileDescriptor

                        val file = File(
                            requireContext().cacheDir,
                            requireContext().contentResolver.getFileName(uri!!)
                        )
                        Log.e("File", file.path.toString())

                        val inputStream = FileInputStream(fileDescriptor)
                        val outputStream = FileOutputStream(file)
                        inputStream.copyTo(outputStream)

                        // creates RequestBody instance from file

                        val requestFile: RequestBody =
                            RequestBody.create("multipart/form-data".toMediaTypeOrNull(), file)
                        // requireContext().create("multipart/form-data".toMediaTypeOrNull(), file)

                        val body: MultipartBody.Part? =
                            MultipartBody.Part.createFormData("image", file.name, requestFile)

                        if (body != null) 
                            arrayList.add(body)
                        
                    

尝试记录 URI 和 FILEPATH ,这里是详细信息

URI: file:///storage/emulated/0/DCIM/Camera/IMG_20210131_150237.jpg
File: /data/user/0/com.visilogix.smarttrax/cache

错误日志

  Caused by: java.io.FileNotFoundException: /data/user/0/com.visilogix.smarttrax/cache: open failed: EISDIR (Is a directory)
    at libcore.io.IoBridge.open(IoBridge.java:496)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:235)
    at java.io.FileOutputStream.<init>(FileOutputStream.java:186)
    at com.visilogix.smarttrax.ui.performPutAway.GrnLinesFragment.onActivityResult(GrnLinesFragment.kt:283)
    at androidx.fragment.app.FragmentActivity.onActivityResult(FragmentActivity.java:170)
    at android.app.Activity.dispatchActivityResult(Activity.java:8110)
    at android.app.ActivityThread.deliverResults(ActivityThread.java:4838)
    at android.app.ActivityThread.handleSendResult(ActivityThread.java:4886) 
    at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51) 
    at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) 
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) 
    at android.os.Handler.dispatchMessage(Handler.java:107) 
    at android.os.Looper.loop(Looper.java:214) 
    at android.app.ActivityThread.main(ActivityThread.java:7356) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930) 
 Caused by: android.system.ErrnoException: open failed: EISDIR (Is a directory)
    at libcore.io.Linux.open(Native Method)

更新了创建文件的代码

  val uri = Uri.parse("file://" + imagesList[i].toString())
                    Log.e("URI", uri.toString())
                    val parcelFileDescriptor: ParcelFileDescriptor? =
                        requireContext().contentResolver.openFileDescriptor(uri, "r")
                    val fileDescriptor: FileDescriptor? = parcelFileDescriptor?.fileDescriptor

                    val uri1 = Uri.parse(imagesList[i].toString())
                    Log.e("File", requireContext().contentResolver.getFileName(uri1!!))
                    val file = File(
                        requireContext().externalCacheDir?.path,
                        requireContext().contentResolver.getFileName(uri1!!)
                    )
                    file.parentFile.mkdir()
                    file.createNewFile()
                    Log.e(
                        "File2",
                        requireContext().contentResolver.getFileName(uri1!!).toString()
                    )

                    val inputStream = FileInputStream(fileDescriptor)
                    val outputStream = FileOutputStream(file)
                    inputStream.copyTo(outputStream)

应用程序在 val outputStream = FileOutputStream(file) 处崩溃。

 Caused by: java.io.FileNotFoundException: /storage/emulated/0/Android/data/com.visilogix.smarttrax/cache: open failed: EISDIR (Is a directory)
    at libcore.io.IoBridge.open(IoBridge.java:496)

【问题讨论】:

该消息告诉您您尝试打开目录而不是文件。可能您在将目录更改为 file.getParentFile().mkdirs(); 之前使用 file.mkdirs() 创建了目录。 @blackapps file.mkdirs() 添加但没有成功 您当然不应该尝试这样做。请重新阅读我的消息并删除目录。或使用其他名称。 requireContext().contentResolver.getFileName(uri1!!) 我们看不到那将是哪个文件名或它是否为空。请使用硬编码的文件名。还要告诉 file.getAbsolutePath() 的值。 普通的图像选择器不会提供这样的文件系统路径,而是提供内容方案 uri。但我当然不是那个。您尝试创建的那个。你有问题的那个。 【参考方案1】:

在创建文件时使用“externalCacheDir”而不是“cacheDir”可能会解决问题。

requireContext().externalCacheDir?.let 
            val file = File(
                    it.path,
                    requireContext().contentResolver.getFileName(uri!!)
            )
            file.createNewFile()

        

【讨论】:

没有修复它。这是使用 externalCacheDir 的文件路径:/storage/emulated/0/Android/data/com.visilogix.smarttrax/cache 抱歉,我错过了在“requireContext().externalCacheDir”中调用的“getPath()”。调用“requireContext().externalCacheDir.path”创建文件,我得到正确的 URI。 引起:java.io.FileNotFoundException:/storage/emulated/0/Android/data/com.visilogix.smarttrax/cache:打开失败:EISDIR(是一个目录)面临同样的问题更新的变化 在创建文件后尝试使用“file.createNewFile()”,因为没有可用的内容,所以无法创建文件。 请查看。我将其发布为该问题的另一个答案。祝你好运!【参考方案2】:

使用 Okhttp 将图像发送到 php 服务器: 首先将所有图像转换为字节数组并将其放入“backupData”列表中。然后调用“backupOneByOne()”方法。

private var client: OkHttpClient = OkHttpClient()

init 
    client.setReadTimeout(5, TimeUnit.MINUTES)
    client.setConnectTimeout(5, TimeUnit.MINUTES)

val backupData: MutableList<ByteArray> = mutableListOf()

private suspend fun backupOneByOne(currentPosition: Int, byteArray: ByteArray) 
    val backupResponse = doBackupMultiPartData("merchantKey", byteArray)

    when (backupResponse) 
        is ResponseResult.Success -> 
            Log.d("nm==>>", "Menu Item backup successful !!! AND current item position= $currentPosition")
            if (currentPosition < backupData.size - 1) 
                backupOneByOne(currentPosition + 1, backupData[currentPosition + 1])
             else 
                Log.d("nm==>>", "Backup of all items is done. Current position= $currentPosition")
            
        
        is ResponseResult.Error -> 
            Log.d("nm==>>", "Error while backup of Advance table : \n $backupResponse.msg.errorMsg")
        
        else -> 
        
    

suspend fun doBackupMultiPartData(apiKey: String, imageData: ByteArray?): ResponseResult<ResponseWrapper<String>> 
    val requestBody = MultipartBuilder().type(MultipartBuilder.FORM)
    //requestBody.addFormDataPart("id",2) put other form data fields
    if (imageData != null) 
        requestBody.addFormDataPart(
                "file", "Logo.png", RequestBody.create(
                MediaType.parse(
                        "image/png"
                ), imageData
        )
        )
    

    val request = Request.Builder()
            .header("api_key", apiKey) // getting api key from backend
            .url("put url here....")
            .post(requestBody.build())
            .build()
    try 
        val result = apiRequest(request)
        Log.d("nm==>>", "Result: $result")
        return if (result != null) 
            val jsonObject = JSONObject(result)
            if (jsonObject.getBoolean("isSuccessful")) 
                ResponseResult.Success(ResponseWrapper("success", null))
             else 
                ResponseResult.Error(
                        ResponseWrapper(null, "Back end code error: \n $result")
                )
            
         else 
            ResponseResult.Error(
                    ResponseWrapper(
                            null, "Getting NULL as result"
                    )
            )
        
     catch (jsonException: JSONException) 
        Log.d("nm==>>", "Restore JSON exception::: \n $jsonException.localizedMessage")
        return ResponseResult.Error(
                ResponseWrapper(
                        null,
                        "Restore Json Exception"
                )
        )
     catch (exception: Throwable) 
        //Log.d("nm==>>", "Network exception::: \n $exception.localizedMessage")
        return ResponseResult.NoInternet
    


private suspend fun apiRequest(request: Request): String? = suspendCancellableCoroutine  cancellableContinuation ->
    client.newCall(request).enqueue(object : Callback 
        override fun onFailure(request: Request?, e: IOException?) 
            cancellableContinuation.resumeWith(Result.failure(Throwable("API failed.....$e?.localizedMessage")))
        

        override fun onResponse(response: Response?) 
            cancellableContinuation.resumeWith(Result.success(response?.body()?.string()))
        
    )

sealed class ResponseResult<out T> 
    object Loading:ResponseResult<Nothing>()
    object Empty:ResponseResult<Nothing>()
    data class Success<T>(val result:T): ResponseResult<T>()
    data class Error<T>(val msg:T): ResponseResult<T>()
    object NoInternet:ResponseResult<Nothing>()

data class ResponseWrapper<out T>(val data: T?, val errorMsg: String?)

【讨论】:

以上是关于如何修复 Kotlin 中 FileInputStream 和 FileDescriptor 的错误路径的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Kotlin 中使用 Timestamp(Date()) 修复此错误

如何修复 io.ktor.server.engine.CommandLineKt 在 gradle/kotlin/netty 项目中抛出的“既未指定端口也未指定 sslPort”?

如何修复“AndroidRuntime: FATAL EXCEPTION: main, kotlin.KotlinNullPointerException”?尝试删除存储的数据Sqlite db时出错

使用 Gradle Kotlin DSL 配置 Jooq 时如何修复“未解析的引用:jdbc”

如何在 Kotlin 中抑制检查式警告

如何从片段 KOTLIN 中调用意图 [重复]