如何在Android中打开文件保存对话框?

8

我有一个Web服务,根据图像ID提供一个字节数组。我想将这些字节数组转换为文件并存储在Android上,就像用户想要保存文件对话框中的文件格式完全相同。

5个回答

18

由于这是谷歌搜索该主题的最高结果,并且在我的研究过程中让我感到非常困惑,因此我认为我应该添加一个更新到这个问题。 自Android 19以来,已经有了内置的保存对话框。你甚至不需要任何权限(甚至不需要WRITE_EXTERNAL_STORAGE)。 它的工作方式非常简单:

//send an ACTION_CREATE_DOCUMENT intent to the system. It will open a dialog where the user can choose a location and a filename

Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("YOUR FILETYPE"); //not needed, but maybe usefull
intent.putExtra(Intent.EXTRA_TITLE, "YOUR FILENAME"); //not needed, but maybe usefull
startActivityForResult(intent, SOME_INTEGER);

...

//after the user has selected a location you get an uri where you can write your data to:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
  if(requestCode == SOME_INTEGER && resultCode == Activity.RESULT_OK) {
    Uri uri = data.getData();

    //just as an example, I am writing a String to the Uri I received from the user:

    try {
      OutputStream output = getContext().getContentResolver().openOutputStream(uri);

      output.write(SOME_CONTENT.getBytes());
      output.flush();
      output.close();
    }
    catch(IOException e) {
      Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show();
    }
  }
}

更多内容请参见:https://developer.android.com/guide/topics/providers/document-provider


我原本以为OutputStream.close()会自动执行flush()。但显然它并没有这样做。谢谢,我会进行编辑的:D - JodliDev
1
我需要使用intent.setType("/")。 否则我会得到"No activity found to handle Intent - android.intent.action.OPEN_DOCUMENT"的错误提示。 - ka3ak
intent.setType("/"); - Jagie

4

Android SDK没有提供自己的文件对话框,因此您需要构建自己的对话框。


4
首先,您需要为保存文件创建一个对话意图。用户选择后,您可以在该目录上进行写入并指定文件,无需任何读/写权限。(自Android 19以来)

Source:https://developer.android.com/training/data-storage/shared/documents-files#create-file

   // Request code for creating a PDF document.
    private final int SAVE_DOCUMENT_REQUEST_CODE = 0x445;
    private File targetFile;
 
   private void createFile() {
            Uri reportFileUri = FileProvider.getUriForFile(getApplicationContext(), getPackageName() + ".provider", targetFile);
        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("application/pdf");
        intent.putExtra(Intent.EXTRA_TITLE, targetFile.getName());
    
        // Optionally, specify a URI for the directory that should be opened in
        // the system file picker when your app creates the document.
        intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
        startActivityForResult(intent, SAVE_DOCUMENT_REQUEST_CODE );
    }

   
        @Override
        protected void onActivityResult(int requestCode, int resultCode, @Nullable 
            Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == SAVE_DOCUMENT_REQUEST_CODE && resultCode == RESULT_OK){
                Uri uri = data.getData();
                saveFile(uri);
            }
        }
     
         private void saveFile(Uri uri) {
                try {
                    OutputStream output = getContentResolver().openOutputStream(uri);
                    FileInputStream fileInputStream = new FileInputStream(targetFile);
                    byte[] bytes = new byte[(int) targetFile.length()];
                    fileInputStream.read(bytes, 0, bytes.length);
                    output.write(bytes);
                    output.flush();
                    output.close();
                    Log.i(TAG, "done");
                } catch (IOException e) {
                    Log.e(TAG, "onActivityResult: ", e);
                }
            }

3

请提供一个可用的链接,用户可以选择存储文件的位置。 - Sushant Bhatnagar
2
创建应用程序中的编辑文本或创建带有编辑文本的对话框。在编辑文本中编写您想要保存文件的路径。通过使用getText()获取路径,并将该对象放入以下位置:File file = new File(extStorageDirectory, "er.PNG")。最终代码将是File file = new File(extStorageDirectory,"OBJECT")。 - himanshu

0

@JodliDev已经提供了被接受的答案,但是startActivityForResult现在已经过时了,所以我想在这里提供我的解决方案,使用registerForActivityResult(ActivityResultContracts.CreateDocument())

首先注册一个ActivityResultLauncher,在其中定义结果应该发生什么。我们将得到uri返回,可以用于我们的OutpuStream。但一定要在开始时初始化它,否则你会得到:

片段必须在创建之前(即初始化、onAttach()或onCreate())调用registerForActivityResult()。

private var ics: String? = null
private val getFileUriForSavingICS = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri ->
    if(ics.isNullOrEmpty())
        return@registerForActivityResult
    try {
        val output: OutputStream? =
            context?.contentResolver?.openOutputStream(uri)
        output?.write(ics?.toByteArray())
        output?.flush()
        output?.close()
    } catch (e: IOException) {
        Toast.makeText(context, "Error", Toast.LENGTH_SHORT).show()
    }
}

然后在需要的地方使用 .launch(...) 调用您的 ActivityResultLauncher。

getFileUriForSavingICS.launch("filename.txt")

就是这样 ;-) 您还可以更详细地了解ActivityResultContracts.CreateDocument()。此方法提供文档保存对话框,但其中还有其他有用的功能(例如启动相机意图)。请查看以下链接: https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts 以获取可能的ActivityResultContracts

或者请参阅https://developer.android.com/training/basics/intents/result以获取更多培训材料和有关如何创建自定义合同的信息!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接