如何将 TXT 文件写入“/storage/emulated/0/myapp/myfile.txt”,以便稍后可以从文件浏览器打开它们?

Seb*_*SBM 8 java android file

我希望我的应用程序生成的 TXT 文件(以及包含它们的文件夹)可以通过手机的文件浏览器访问,甚至可以在通过 USB 插入手机时从 PC 访问。Android 的官方文档描述了不同类型的存储,具体取决于它是“内部”还是“外部”(Google 的术语)。当然,我不希望在卸载应用程序时删除这些 TXT 文件。

为避免任何混淆:

我想对应用程序中的文件位置进行硬编码这一事实并不意味着我必须使用完整的绝对文件路径作为参数。我的意思是相对文件路径将被硬编码(相同的任务,相同的文件,相同的位置)。

……那是说……

该应用程序供个人使用(我的意思是我自己使用它),因此满足 Google Play 商店的要求目前不是首要任务。

代码示例(我尝试过的)

 try {

     // Dedicated folder would be better...
     // ...but not even in the root folder works...
     // FIXME T_T
     FileWriter writer = new FileWriter("/storage/emulated/0/myfile.txt");
     writer.write(my_string_for_file);
     writer.flush();
     writer.close();
 } catch (IOException e) {
     e.printStackTrace();
 }
// ...always throws FileNotFoundException with Permission DENIED
Run Code Online (Sandbox Code Playgroud)

这将是 PC 上的 Python,5 分钟内就完成了!3个小时了我还是没明白??我想哭...

所以,我想始终将一些输出保存在同一个文件、同一个路径中。我希望当我通过 USB 插入电脑时可以从我的电脑以及手机的文件浏览器访问该 TXT 文件。例如,在 Python 中,对于 PC 应用程序,我将使用 3 行代码来实现这一点!在 Android 中这不会更困难,对吧?(或者我希望如此)

我知道,对于我在这里所描述的,它在技术上是“文档和其他类型的文件”,以及 Google 或 Android 开发人员所认为的“外部存储”(但 Android 用户认为是内部存储!)。嗯,Android 开发人员文档在此链接中有一个相关部分。

但该示例的代码片段对于我的需求来说太过分了:我不希望用户选择文件路径:我想将 TXT 文件的路径硬编码到/storage/emulated/0/myapp/目录中。相反,该示例似乎提供了某种方法来打开某种“另存为”文件对话框,这是我不想要的

根据这篇文章,它建议我使用Environment.getExternalStoragePublicDirectory,如果我查看此方法的文档,它会说此方法已从 API 28 中弃用。我一直在查看有关该主题的其他文档,但我仍然对如何继续感到困惑。假设我已经有了要写入String变量的文本:

  1. 我如何确保文件将写入某个专用文件夹,例如/storage/emulated/0/myapp/storage/emulated/0/myfolder而不是根文件夹/storage/emulated/0?会getExternalFilesDir()返回/storage/emulated/0还是有更好的方法?

  2. 哪个是最简单的代码,只需将包含String变量文本和硬编码文件名的文件写入专用目录,但在卸载应用程序后它仍然会存在?

如果是 或/storage/emulated/0/storage/emulated/0/Documents两者中的任何一个对我来说都可以。最重要的是,我可以从我的应用程序外部访问这些文件,如果可能的话,卸载上述应用程序不会删除它们。

que*_*gre 3

Context.getExternalFilesDir(null)是新的替代方案,Environment.getExternalStoragePublicDirectory()但正如您所提到的,这将返回一个目录,一旦您卸载应用程序,该目录将被删除。对于你需要使用的用例,MediaStore我完全同意你的观点:这已经变得非常复杂(我认为 android 这样做是为了加强安全性,以便其他应用程序可以更轻松地浏览你放置在共享目录中的文件)。以下是使用媒体商店执行您想要的操作的一些方法:

private void saveTxtFile(String fileName, String fileContent, Context context) throws IOException {
        OutputStream fos;
        try{
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                
                ContentValues values = new ContentValues();

                values.put(MediaStore.MediaColumns.DISPLAY_NAME, fileName);       //file name
                values.put(MediaStore.MediaColumns.MIME_TYPE, "text/plain");        //file extension, will automatically add to file
                values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS + "/yourFolder/");     //end "/" is not mandatory

                Uri uri = context.getContentResolver().insert(MediaStore.Files.getContentUri("external"), values);      //important!

                fos = context.getContentResolver().openOutputStream(uri);
            } else {
                String docsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString();
                File file = new File(docsDir, fileName);
                fos=new FileOutputStream(file);
            }
            try {
                fos.write(fileContent.getBytes());
            } finally {
                fos.close();
            }
        }catch (IOException e){
            e.printStackTrace();
        }
       
    }
Run Code Online (Sandbox Code Playgroud)

您还需要以下方法来确定文件是否已经存在。我做到了,所以它返回一个 Uri,但你应该更改它以满足你的需要

  private Uri isFilePresent(String fileName, Context context) {
    Uri contentUri = MediaStore.Files.getContentUri("external");

    String selection = MediaStore.MediaColumns.RELATIVE_PATH + "=?";

    String[] selectionArgs = new String[]{Environment.DIRECTORY_DOCUMENTS + "/yourFolder/"};

    Cursor cursor = context.getContentResolver().query(contentUri, null, selection, selectionArgs, null);

    Uri uri = null;

    if (cursor.getCount() == 0) {
        Toast.makeText(context, "No file found in " + Environment.DIRECTORY_DOCUMENTS + "yourFolder", Toast.LENGTH_LONG).show();
    } else {
        while (cursor.moveToNext()) {
            String file = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME));

            if (fileName.equals(fileName)) {
                long id = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID));

                uri = ContentUris.withAppendedId(contentUri, id);

                break;
            }
        }
    }
return uri;
}
Run Code Online (Sandbox Code Playgroud)

请不要忘记将相应的权限添加到清单中以使其正常工作:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Run Code Online (Sandbox Code Playgroud)

顺便说一句,这个答案做了类似的事情