web-dev-qa-db-ja.com

Android kotlin:ファイルピッカーから選択されたファイル名を使用したFileNotFoundExceptionの取得]

私はAndroidアプリケーションで取り組んでいます。機能の1つがユーザーに開くファイルを選択できるようにする(プレーンテキスト.txtファイルを開きたい)。私はJavaと前にAndroidアプリで働いていましたが、これはKotlinを使っています、そしてそれは私の初めてKotlinを使っています。

私は現在、アプリがファイルの選択を表示していて、ユーザーに開きたいファイルをタップさせることができます。それから私はファイルオブジェクトを使ってファイルを開き、foreachlineループを実行しようとしています。しかし、何らかの理由で、ファイルピッカーから選択されたファイルを使用して、java.io.fileNotFoundException(そのようなファイルまたはディレクトリはありません)を投げます。何が問題なのかわからない、そしてファイルパスを変換するために何らかの変換をしなければならない場合は?

私の「ロード」ボタンのコード:

_val btn_load: Button = findViewById<Button>(R.id.btn_load_puzzle)
    btn_load.setOnClickListener {
        val intent = Intent()
            .setType("*/*")
            .setAction(Intent.ACTION_GET_CONTENT)

        startActivityForResult(Intent.createChooser(intent, "Select a file"), 111)
    }
_

ファイル選択に応答するための私の関数:

_override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    // Selected a file to load
    if ((requestCode == 111) && (resultCode == RESULT_OK)) {
        val selectedFilename = data?.data //The uri with the location of the file
        if (selectedFilename != null) {
            val filenameURIStr = selectedFilename.toString()
            if (filenameURIStr.endsWith(".txt", true)) {
                val msg = "Chosen file: " + filenameURIStr
                val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_SHORT)
                toast.show()
                File(selectedFilename.getPath()).forEachLine {
                    val toast = Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT)
                    toast.show()
                }
            }
            else {
                val msg = "The chosen file is not a .txt file!"
                val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG)
                toast.show()
            }
        }
        else {
            val msg = "Null filename data received!"
            val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG)
            toast.show()
        }
    }
}
_

FileChlineループを実行するファイルオブジェクトを作成する行にFileNotFoundの例外がスローされます。

Java.lang.RuntimeException:失敗結果resultInfo {who = null、要求= 111、result = -1、data = Intent {dat = content://com.android.externalstorage.documents/0000-0000:数独パズル/hard001.txt flg = 0x1}}アクティビティ{com.example.sudokusolver/com.example.sudokusolver.mainactivity}:java.io.fileNotFoundException:/ document/0000-0000:Sudokuパズル/ hard001.txt(そのようなファイルなしまたはディレクトリ)

7
CalicoSkies

ファイルパスを受け取らず、Uriを受け取りました。 ContentResolver.openInputStream() のようなUriベースのAPIを使用して、その内容にアクセスする必要がありますUri AndroidとしてApp Direct Fileを基礎となるファイルへのアクセスを許可しません(それはGoogleドライブからストリーミングするか、またはこれが起こっていることを知らずにインターネットから直接ダウンロードすることもできます)。 :

_override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    // Selected a file to load
    if ((requestCode == 111) && (resultCode == RESULT_OK)) {
        val selectedFilename = data?.data //The uri with the location of the file
        if (selectedFilename != null) {
            contentResolver.openInputStream(selectedFilename)?.bufferedReader()?.forEachLine {
                val toast = Toast.makeText(applicationContext, it, Toast.LENGTH_SHORT)
                toast.show()
            }
        } else {
            val msg = "Null filename data received!"
            val toast = Toast.makeText(applicationContext, msg, Toast.LENGTH_LONG)
            toast.show()
        }
    }
}
_

ここでは、適切なMIMEタイプを要求に渡すことで、適切なフォーマットの内容を取得することができます(テキストファイルがそのパスの一部として_.txt_拡張子を正確に終了する必要がないため、)。

_val intent = Intent()
    .setType("text/*")
    .setAction(Intent.ACTION_GET_CONTENT)

startActivityForResult(Intent.createChooser(intent, "Select a file"), 111)
_

これは自動的にテキストファイルを選択できません。

6
ianhanniballake

URIで "msf:xxx"を取得している場合は、App CacheディレクトリにTEMPファイルを作成し、タスクが完了した後に同じファイルを削除しました。

if (id != null && id.startsWith("msf:")) {
                    final File file = new File(mContext.getCacheDir(), Constant.TEMP_FILE + Objects.requireNonNull(mContext.getContentResolver().getType(imageUri)).split("/")[1]);
                    try (final InputStream inputStream = mContext.getContentResolver().openInputStream(imageUri); OutputStream output = new FileOutputStream(file)) {
                        final byte[] buffer = new byte[4 * 1024]; // or other buffer size
                        int read;

                        while ((read = inputStream.read(buffer)) != -1) {
                            output.write(buffer, 0, read);
                        }

                        output.flush();
                        return file;
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    return null;
                }
 _

私はこの問題を修正しました、そしてそれはMSFのための100%働いています。 :)

作業の完了後にTEMPファイルも削除してください。

private void deleteTempFile() {
        final File[] files = requireContext().getCacheDir().listFiles();
        if (files != null) {
            for (final File file : files) {
                if (file.getName().contains(Constant.TEMP_FILE)) {
                    file.delete();
                }
            }
        }
    }
 _

ここでtemp_fileの値は "tempです"です。

2
SANAT

URIを指定してビットマップファイルを開きます。

private Bitmap getBitmapFromUri(Uri uri) throws IOException {
    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r");
    FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
    Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);
    parcelFileDescriptor.close();
    return image;
}
 _
0
miraquee