通过FileProvider和Intent将缓存文件附加到GMail不起作用



所以在过去的一天里,我一直在用头撞墙,试图弄清楚为什么文件不会附加到电子邮件中。每次应用程序运行时,我都会收到一条祝酒词,上面写着";无法附加文件;。"收件人"one_answers"主题"字段的填充与我预期的一样。第一个问题是,我如何才能找到这个错误背后的进一步信息?这条消息是从GMail应用程序抛出的,而不是我自己的程序。它肯定会给我指明正确的方向,这样如果我有这个错误的原因,我就可以自己进一步调试了。我在下面包含了可能相关的信息。文件大小为78.5kb,我可以验证该文件是否存在,并且具有我想要附加的正确内容。根据文件资源管理器,文件的权限是所有者和组的rw。

我在打这篇文章时发现了一些有趣的东西;当使用文件资源管理器在gmail应用程序中添加附件时,Android/data目录不是一个选项!不过,当你在gmail应用程序之外使用文件资源管理器时,它会显示出来。所以我认为这可能是权限问题?它无法访问此文件夹。如果是这种情况,建议在什么位置存储此文件?理想情况下,在这种情况下,它将是某种缓存位置或临时文件位置。

我试着在Android Outlook应用程序中添加附件,而不是GMail,文件确实被附加了。

尝试使用Pixel 4 Emulator在Android 11 API 30上运行。

电子邮件意向代码:

protected void sendEmail(File f){
final String[] TO = { "foo@bar.com" };
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
emailIntent.setData(Uri.parse("mailto:"));
emailIntent.putExtra(Intent.EXTRA_EMAIL, TO);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, f.getName().replaceAll("(?i).pdf", ""));
if (!f.exists() || !f.canRead()) {
Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show();
finish();
return;
}
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri uri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID, f);
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
try {
startActivity(emailIntent);
finish();
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(MainActivity.this,
"There is no email client installed.", Toast.LENGTH_SHORT).show();
}

AndroidManifest.xml:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>

provider_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-cache-path name="external_cache" path="."/>
</paths>

f:的值

/storage/emulated/0/Android/data/com.bar.foo/cache/Form2020-12-27.pdf

uri路径的值:

content://com.bar.foo/external_cache/Form2020-12-27.pdf

经过进一步的研究,我发现了"解决方案";。它可能在某些情况下不适用,但我追求的结果恰好有效。它与意向数据和类型有关。我已经将意向设置更改为以下内容,保留所有其他项目不变:

protected void sendEmail(File f){
final String[] TO = { "foo@bar.com" };
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.putExtra(Intent.EXTRA_EMAIL, TO);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, f.getName().replaceAll("(?i).pdf", ""));
if (!f.exists() || !f.canRead()) {
Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show();
finish();
return;
}
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID, f);
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
emailIntent.setDataAndType(uri, getContentResolver().getType(uri));
emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
try {
startActivity(emailIntent);
finish();
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(MainActivity.this,
"There is no email client installed.", Toast.LENGTH_SHORT).show();
}
}

我将Intent操作从action_SENDTO更改为action_SEND仅凭这一点并不能解决问题。

代替设置意图数据并用";mailto:";,我从uri中设置了数据和类型。

如果未设置默认应用程序,则会弹出一个选择器,询问用户要将哪些相关应用程序与文件一起使用。不过,这显示的不仅仅是电子邮件客户端,这对于某些应用程序来说可能并不理想。这并不是我设计的初衷,但考虑到我的用户可能想将表单附加到非电子邮件客户端,如MS Teams或云存储客户端,这将起作用。点击GMail将文件附加到电子邮件中,并按预期设置主题和收件人。

请改用ACTION_SEND

TO也将在这里实现。

添加:

emailIntent.setType("message/rfc822");

您可以移除setData((调用;

我的问题是将PDF文件保存到外部文件路径(不是外部缓存路径(。我想要么Gmail无法从那里获取文件,要么你无法共享外部文件目录中的文件。将文件保存/检索目录更改为外部缓存解决了我的问题。

顺便说一下,我的意图类型是";application/pdf";。我没有使用";消息/rfc822";

由于支持不同的设备,我遇到了一些问题。这就是我想出的解决方案:

Intent intent = new Intent(Intent.ACTION_SEND);
Uri data = Uri.parse("mailto:" + Uri.encode(mailAddress)
+ "?subject=" + Uri.encode(subject)
+ "&body=" + Uri.encode(message));
intent.setData(data);
// yes this is duplicate intended code but this ensures more compatibility for different devices
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{mailAddress});
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, message);
String filePath = BaseApplication.getAppContext().getFilesDir().toString() + "/" + FILENAME;
File attachment = new File(filePath);
attachment.setReadable(true, false);
try {
Uri u = FileProvider.getUriForFile(this,
Constants.FILE_PROVIDER_AUTHORITY,
attachment);
intent.putExtra(Intent.EXTRA_STREAM, u);
intent.setType("message/rfc822");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Error while providing attachment: " + attachment, e);
}
startActivity(intent);

此外,还需要一个FileProvider,就像问题中已经提到的那样。

对于提问者来说,最终有点晚了,但希望它能帮助其他人!

最新更新