我正在开发一个Android应用程序,其功能之一是让用户选择要打开的文件(我想打开纯文本.txt文件)。在使用Java之前,我曾经从事过Android应用程序的工作,但是对于这个应用程序,我正在使用Kotlin,这是我第一次使用Kotlin。
我目前让该应用显示一个文件选择器,并让用户点击他们想要打开的文件。然后,我尝试使用File对象打开文件并执行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()
}
}
}
在创建File对象以执行forEachLine循环的行上抛出FileNotFound异常:
java.lang.RuntimeException:无法传递结果ResultInfo {who = null,request = 111,result = -1,data = Intent {dat = content://com.android.externalstorage.documents/document/0000-0000 :Sudoku puzzles / hard001.txt flg = 0x1}}到活动{com.example.sudokusolver / com.example.sudokusolver.MainActivity}:java.io.FileNotFoundException:/ document / 0000-0000:Sudoku puzzles / hard001.txt(没有此类文件或目录)
答案 0 :(得分:2)
您没有收到文件路径,您收到了Uri
。您必须使用基于Uri
的API,例如ContentResolver.openInputStream()
来访问该Uri
上的内容,因为Android不会授予您的应用直接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)
这将自动使所有非文本文件无法被选择。
答案 1 :(得分:2)
如果您在URI中获得“ msf:xxx”,请使用以下解决方案,其中我已在应用程序缓存目录中创建了临时文件,并在完成任务后删除了该文件:
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%。 :)
在完成工作后还删除临时文件:
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的值为“温度”。
答案 2 :(得分:0)
您无法在转换为字符串的ÙRI
上打开Java文件,URI的“路径”部分与物理文件位置无关。
使用contentResolver
获取Java FileDescriptor
来打开文件。
val parcelFileDescriptor: ParcelFileDescriptor =
contentResolver.openFileDescriptor(uri, "r")
val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
此方法与Android 10兼容,在Android 10上,非App私有目录的文件路径不可用。
https://developer.android.com/training/data-storage/shared/documents-files
答案 3 :(得分:0)
打开一个给定 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;
}