内部存储电子邮件

时间:2011-05-20 13:50:33

标签: java android email

在我的应用程序中,我将文件写入内部存储,如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.."));

错误是

  

file://附件路径必须指向file:// mnt / sdcard。忽略附件文件:// ...

7 个答案:

答案 0 :(得分:24)

我认为您可能在Android Gmail客户端中发现了一个错误(或至少是不必要的限制)。我能够解决它,但它对我来说太具体了,并且需要更多的工作才能移植:

First CommonsWare非常正确,需要使文件可读:

fos = openFileOutput(xmlFilename, MODE_WORLD_READABLE);

接下来,我们需要解决Gmail对/ mnt / sdcard(或特定于实现的等效?)路径的坚持:

Uri uri = Uri.fromFile(new File("/mnt/sdcard/../.."+getFilesDir()+"/"+xmlFilename));

至少在我修改过的Gingerbread设备上,这是一个从私人存储器到我自己的Gmail附件,并在收到时使用预览按钮查看内容。但是我不觉得必须这样做才能使它工作,并且知道在其他版本的Gmail或其他电子邮件客户端或将外部存储装置安装在其他地方的手机会发生什么情况会非常“好”。

答案 1 :(得分:11)

我最近一直在努力解决这个问题,我想从支持库中使用FileProvider分享我找到的解决方案。它是内容提供商的扩展,可以很好地解决这个问题而无需解决方法,而且不是太多的工作。

如链接中所述,要激活内容提供商: 在你的清单中,写下:

<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>
    ...

元数据应该指示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>

使用内部文件文件夹时路径为空,但如果是更一般的位置(我们现在讨论的是内部存储路径),则应使用其他路径。您编写的名称将用于内容提供商提供给该文件的URL。

现在,您只需使用以下命令即可生成一个全新的,可读取的网址:

Uri contentUri = FileProvider.getUriForFile(context, "com.yourdomain.yourapp.fileprovider", file);

来自res / xml / file_paths.xml元数据中路径的任何文件。

现在只需使用:

    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();
    }

您不需要提供权限,您可以在将网址附加到文件时自动执行此操作。

并且您不需要将文件设置为MODE_WORLD_READABLE,现在不推荐使用此模式,使其成为MODE_PRIVATE,内容提供商会为其他应用程序可访问的同一文件创建新的URL。

我应该注意,我只在使用Gmail的模拟器上测试过它。

答案 2 :(得分:7)

Chris Stratton提出了良好的解决方法。然而,它在许多设备上失败了。你不应该硬编码/ mnt / sdcard路径。你最好计算它:

String sdCard = Environment.getExternalStorageDirectory().getAbsolutePath();
Uri uri = Uri.fromFile(new File(sdCard + 
          new String(new char[sdCard.replaceAll("[^/]", "").length()])
                    .replace("\0", "/..") + getFilesDir() + "/" + xmlFilename));

答案 3 :(得分:5)

考虑到此处的建议:http://developer.android.com/reference/android/content/Context.html#MODE_WORLD_READABLE,自API 17起,我们鼓励使用ContentProviders等。 感谢那个人和他的帖子http://stephendnicholas.com/archives/974我们有一个解决方案:

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; }
}

在内部缓存中创建文件:

    File tempDir = getContext().getCacheDir();
    File tempFile = File.createTempFile("your_file", ".txt", tempDir);
    fout = new FileOutputStream(tempFile);
    fout.write(bytes);
    fout.close();

设置意图:

...
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + tempFile.getName()));

在AndroidManifest文件中注册Content provider:

<provider android:name="CachedFileProvider" android:authorities="com.yourpackage.gmailattach.provider"></provider>

答案 4 :(得分:5)

File.setReadable(true, false);

为我工作。

答案 5 :(得分:1)

错误是特定的:您应该使用external storage中的文件来制作附件。

答案 6 :(得分:1)

如果您要使用内部存储,请尝试使用确切的存储路径:

Uri uri = Uri.fromFile(new File(context.getFilesDir() + File.separator + xmlFilename));

或者另外继续在调试器中更改文件名,并在每个文件上调用“new File(blah).exists()”以快速查看文件的确切名称

它也可能是您设备特有的实际设备实施问题。你尝试过使用其他设备/模拟器吗?