SAF - 使用 ACTION_CREATE_DOCUMENT 创建后通过邮件共享文件

Posted

技术标签:

【中文标题】SAF - 使用 ACTION_CREATE_DOCUMENT 创建后通过邮件共享文件【英文标题】:SAF - Share file via mail after creation with ACTION_CREATE_DOCUMENT 【发布时间】:2021-03-23 17:52:47 【问题描述】:

我将我的 targetSdkVersion 更新为 30,因此我必须使用 SAF(存储访问框架)更新我的创建文件。

我能够创建文件、选择位置、获取 Uri 结果(格式为 content://),但是当我尝试将文件作为附件发送时,文件未附加。例如,Gmail 会显示一条消息“无法附加文件”。

我错过了什么吗?

任何帮助将不胜感激。谢谢大家

这是我的代码:


int WRITE_REQUEST_CODE = 336;

    private void createFile() 
        Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.setType("application/pdf");
        intent.putExtra(Intent.EXTRA_TITLE, "dettaglio_utente.pdf");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivityForResult(intent, WRITE_REQUEST_CODE);
    

    private void alterDocument(Uri uri) 
        try 
            ParcelFileDescriptor pfd = getContentResolver().
                    openFileDescriptor(uri, "w");
            FileOutputStream fileOutputStream =
                    new FileOutputStream(pfd.getFileDescriptor());
            byte[] pdfAsBytes = Base64.decode(pdfBase64, 0);
            fileOutputStream.write(pdfAsBytes);
            // Let the document provider know you're done by closing the stream.
            fileOutputStream.close();
            pfd.close();
         catch (FileNotFoundException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
    

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) 
        if (resultCode == RESULT_OK && requestCode == WRITE_REQUEST_CODE) 
            try 

                if (data != null && data.getData() != null) 
                    Uri path = data.getData();
                    alterDocument(path);

                    Intent intent = new Intent(Intent.ACTION_SEND);
                    intent.setData(Uri.parse("mailto:")); // only email apps should handle this
                    intent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.privacy_mail_subject));
                    intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.privacy_mail_body));
                    intent.putExtra(Intent.EXTRA_STREAM, path);
                    if (intent.resolveActivity(getPackageManager()) != null) 
                        startActivity(intent);
                    
                
             catch (Exception e) 
                Toast.makeText(this, "something went wrong" + e.getMessage(), Toast.LENGTH_SHORT).show();
                e.printStackTrace();
            
        
    

我还在 Manifest 中添加了 DocumentProvider,如 android 文档 StorageProvider 所示

<provider
            android:name=".MyDocumentProvider"
            android:authorities="$applicationId.documents"
            android:enabled="true"
            android:exported="true"
            android:grantUriPermissions="true"
            android:permission="android.permission.MANAGE_DOCUMENTS">
            <intent-filter>
                <action android:name="android.content.action.DOCUMENTS_PROVIDER" />
            </intent-filter>
        </provider>

【问题讨论】:

【参考方案1】:

当您提供Uri 给其他人时,您添加FLAG_GRANT_READ_URI_PERMISSION 和/或FLAG_GRANT_READ_URI_PERMISSION

所以,关于你的代码:

您可以从您的createFile() 方法中删除intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);,因为您希望接收 Uri

在使用Intent 之前,您需要将intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 添加到您的ACTION_SEND Intent,因为您希望提供一个Uri,并且您希望收件人能够读取那个Uri标识的内容

还有:

从您的 &lt;provider&gt; 中删除 android:permission="android.permission.MANAGE_DOCUMENTS",因为其他应用(例如 Gmail)很可能不会拥有该权限

请注意,如果不在清单中添加 &lt;queries&gt; 元素,resolveActivity() 可能无法在 Android 11+ 上运行 - 您可能希望跳过 resolveActivity() 调用,只需将 startActivity() 调用包装在 try/ 中catch和处理ActivityNotFoundException

【讨论】:

感谢@CommonsWare 的回复,我删除了android:permission="android.permission.MANAGE_DOCUMENTS",但它在启动时给了我SecurityException。所以我这样解决:我从 Manifest 中删除了所有节点 &lt;provider&gt;,然后我从电子邮件意图 intent.setData(Uri.parse("mailto:")); 中删除了以下行,并将其替换为 intent.setType("message/rfc822"); 现在它可以工作了!!

以上是关于SAF - 使用 ACTION_CREATE_DOCUMENT 创建后通过邮件共享文件的主要内容,如果未能解决你的问题,请参考以下文章

SAF(Storage Access Framework)使用攻略

在 Android 中从 File Api 迁移到 Storage Access Framework (SAF)

额外启用带有存储访问框架 (SAF) 的显示/隐藏 SD 卡

Android_存储访问框架SAF

使用 SAF 访问 Google 驱动器时如何刷新内容

Spring整合Weblogic jms实战