java 来自内部存储的电子邮件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6072895/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Email from internal storage
提问by Mr Hymanson
On my application I write a file to the internal storage as covered on android developer. Then later on I wish to email the file I wrote into the internal storage. Here is my code and the error I am getting, any help will be appreciated.
在我的应用程序中,我将文件写入内部存储,如android developer 中所述。然后稍后我希望通过电子邮件发送我写入内部存储的文件。这是我的代码和我得到的错误,任何帮助将不胜感激。
FileOutputStream fos = openFileOutput(xmlFilename, MODE_PRIVATE);
fos.write(xml.getBytes());
fos.close();
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("text/plain");
...
Uri uri = Uri.fromFile(new File(xmlFilename));
intent.putExtra(android.content.Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, "Send eMail.."));
And the error is
错误是
file:// attachment path must point to file://mnt/sdcard. Ignoring attachment file://...
file:// 附件路径必须指向 file://mnt/sdcard。忽略附件文件://...
回答by Chris Stratton
I think you may have found a bug (or at least unnecessary limitation) in the android Gmail client. I was able to work around it, but it strikes me as too implementation specific, and would need a little more work to be portable:
我认为您可能在 android Gmail 客户端中发现了一个错误(或至少是不必要的限制)。我能够解决它,但它让我觉得太特定于实现,并且需要更多的工作才能移植:
First CommonsWare is very much correct about needing to make the file world readable:
First CommonsWare 关于需要使文件世界可读是非常正确的:
fos = openFileOutput(xmlFilename, MODE_WORLD_READABLE);
Next, we need to work around Gmail's insistence on the /mnt/sdcard (or implementation specific equivalent?) path:
接下来,我们需要解决 Gmail 对 /mnt/sdcard(或实现特定的等效?)路径的坚持:
Uri uri = Uri.fromFile(new File("/mnt/sdcard/../.."+getFilesDir()+"/"+xmlFilename));
At least on my modified Gingerbread device, this is letting me Gmail an attachment from private storage to myself, and see the contents using the preview button when I receive it. But I don't feel very "good" about having to do this to make it work, and who knows what would happen with another version of Gmail or another email client or a phone which mounts the external storage elsewhere.
至少在我修改过的 Gingerbread 设备上,这让我可以将私人存储中的附件通过 Gmail 发送给我自己,并在我收到时使用预览按钮查看内容。但是我对必须这样做才能使其工作感到不太“好”,谁知道另一个版本的 Gmail 或另一个电子邮件客户端或将外部存储安装在其他地方的手机会发生什么。
回答by Yaniv006
I have been struggling with this issue lately and I would like to share the solution I found, using FileProvider, from the support library. its an extension of Content Provider that solve this problem well without work-around, and its not too-much work.
我最近一直在努力解决这个问题,我想分享我使用FileProvider从支持库中找到的解决方案。它是 Content Provider 的扩展,无需变通就可以很好地解决这个问题,而且工作量也不大。
As explained in the link, to activate the content provider: in your manifest, write:
如链接中所述,要激活内容提供程序:在您的清单中,写入:
<application
....
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.youdomain.yourapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
...
the meta data should indicate an xml file in res/xml folder (I named it file_paths.xml):
元数据应指示 res/xml 文件夹中的 xml 文件(我将其命名为 file_paths.xml):
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path path="" name="document"/>
</paths>
the path is empty when you use the internal files folder, but if for more general location (we are now talking about the internal storage path) you should use other paths. the name you write will be used for the url that the content provider with give to the file.
当您使用内部文件文件夹时,路径为空,但如果是更一般的位置(我们现在谈论的是内部存储路径),您应该使用其他路径。您编写的名称将用于内容提供者提供给文件的 url。
and now, you can generate a new, world readable url simply by using:
现在,您只需使用以下命令即可生成一个新的、世界可读的 url:
Uri contentUri = FileProvider.getUriForFile(context, "com.yourdomain.yourapp.fileprovider", file);
on any file from a path in the res/xml/file_paths.xml metadata.
在 res/xml/file_paths.xml 元数据中路径的任何文件上。
and now just use:
现在只需使用:
Intent mailIntent = new Intent(Intent.ACTION_SEND);
mailIntent.setType("message/rfc822");
mailIntent.putExtra(Intent.EXTRA_EMAIL, recipients);
mailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
mailIntent.putExtra(Intent.EXTRA_TEXT, body);
mailIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
try {
startActivity(Intent.createChooser(mailIntent, "Send email.."));
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(this, R.string.Message_No_Email_Service, Toast.LENGTH_SHORT).show();
}
you don't need to give a permission, you do it automatically when you attach the url to the file.
您不需要授予权限,当您将 url 附加到文件时,您会自动授予权限。
and you don't need to make your file MODE_WORLD_READABLE, this mode is now deprecated, make it MODE_PRIVATE, the content provider creates new url for the same file which is accessible by other applications.
并且您不需要将文件设为 MODE_WORLD_READABLE,此模式现已弃用,将其设为 MODE_PRIVATE,内容提供商会为其他应用程序可访问的同一文件创建新 url。
I should note that I only tested it on an emulator with Gmail.
我应该注意,我只在带有 Gmail 的模拟器上对其进行了测试。
回答by Dmitry Kochin
Chris Stratton proposed good workaround. However it fails on a lot of devices. You should not hardcode /mnt/sdcard path. You better compute it:
Chris Stratton 提出了很好的解决方法。但是它在很多设备上都失败了。你不应该硬编码 /mnt/sdcard 路径。你最好计算一下:
String sdCard = Environment.getExternalStorageDirectory().getAbsolutePath();
Uri uri = Uri.fromFile(new File(sdCard +
new String(new char[sdCard.replaceAll("[^/]", "").length()])
.replace("public class CachedFileProvider extends ContentProvider {
public static final String AUTHORITY = "com.yourpackage.gmailattach.provider";
private UriMatcher uriMatcher;
@Override
public boolean onCreate() {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "*", 1);
return true;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
switch (uriMatcher.match(uri)) {
case 1:// If it returns 1 - then it matches the Uri defined in onCreate
String fileLocation = AppCore.context().getCacheDir() + File.separator + uri.getLastPathSegment();
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY);
return pfd;
default:// Otherwise unrecognised Uri
throw new FileNotFoundException("Unsupported uri: " + uri.toString());
}
}
@Override public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { return 0; }
@Override public int delete(Uri uri, String s, String[] as) { return 0; }
@Override public Uri insert(Uri uri, ContentValues contentvalues) { return null; }
@Override public String getType(Uri uri) { return null; }
@Override public Cursor query(Uri uri, String[] projection, String s, String[] as1, String s1) { return null; }
}
", "/..") + getFilesDir() + "/" + xmlFilename));
回答by far.be
Taking into account recommendations from here: http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE, since API 17 we're encouraged to use ContentProviders etc. Thanks to that guy and his post http://stephendnicholas.com/archives/974we have a solution:
考虑到这里的建议:http: //developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE,自 API 17 以来,我们鼓励我们使用 ContentProviders 等。感谢那个人和他的帖子http: //stephendnicholas.com/archives/974我们有一个解决方案:
File tempDir = getContext().getCacheDir();
File tempFile = File.createTempFile("your_file", ".txt", tempDir);
fout = new FileOutputStream(tempFile);
fout.write(bytes);
fout.close();
Than create file in Internal cache:
比在内部缓存中创建文件:
...
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + tempFile.getName()));
Setup Intent:
设置意图:
<provider android:name="CachedFileProvider" android:authorities="com.yourpackage.gmailattach.provider"></provider>
And register Content provider in AndroidManifest file:
并在 AndroidManifest 文件中注册 Content provider:
File.setReadable(true, false);
回答by RobinBobin
Uri uri = Uri.fromFile(new File(context.getFilesDir() + File.separator + xmlFilename));
worked for me.
对我来说有效。
回答by Vladimir Ivanov
The error is enough specific: you should use file from external storagein order to make an attachment.
该错误足够具体:您应该使用外部存储中的文件来制作附件。
回答by David T.
If you are going to use internal storage, try to use the exact storage path:
如果您打算使用内部存储,请尝试使用确切的存储路径:
##代码##or additionally keep changing the file name in the debugger and just call "new File(blah).exists()" on each of the file to see quickly what exact name is your file
或者另外在调试器中不断更改文件名,只需在每个文件上调用“new File(blah).exists()”即可快速查看您的文件的确切名称
it could also be an actual device implementation problem specific to your device. have you tried using other devices/emulator?
它也可能是特定于您的设备的实际设备实现问题。您是否尝试过使用其他设备/模拟器?