Подтвердить что ты не робот

Электронная почта с внутреннего хранилища

В моем приложении я пишу файл во внутреннюю память, как описано в разработчике Android. Затем позже я хочу отправить по электронной почте файл, который я написал во внутреннем хранилище. Вот мой код и ошибка, которую я получаю, любая помощь будет оценена.

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://путь вложения должен указывать на файл://mnt/sdcard. Игнорирование файла вложения://...

4b9b3361

Ответ 1

Я думаю, что вы, возможно, обнаружили ошибку (или, по крайней мере, ненужное ограничение) в клиенте 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 или другим почтовым клиентом или телефоном, который монтирует внешнее хранилище в другом месте.

Ответ 2

В последнее время я борется с этой проблемой, и я хотел бы поделиться решением, которое я нашел, используя FileProvider, из поддержки библиотека. его расширение Content Provider, которое эффективно решает эту проблему без работы, и не слишком много работает.

Как поясняется в ссылке, для активации поставщика контента: в своем манифесте напишите:

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

метаданные должны указывать файл xml в папке res/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-адреса, предоставленного поставщиком контента с предоставлением файла.

и теперь вы можете создать новый, читаемый в мире 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();
    }

вам не нужно давать разрешение, вы делаете это автоматически, когда вы прикрепляете url к файлу.

и вам не нужно делать свой файл MODE_WORLD_READABLE, этот режим теперь устарел, сделайте его MODE_PRIVATE, поставщик контента создаст новый URL-адрес для того же файла, доступного другим приложениям.

Я должен отметить, что я тестировал его только на эмуляторе с Gmail.

Ответ 3

Крис Страттон предложил хорошее обходное решение. Однако он не работает на многих устройствах. Вы не должны указывать путь hardcode/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));

Ответ 4

Принимая во внимание рекомендации отсюда: 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:

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

Ответ 5

File.setReadable(true, false);

работал у меня.

Ответ 6

Ошибка достаточно конкретна: вы должны использовать файл из внешнего хранилища для создания вложения.

Ответ 7

Если вы собираетесь использовать внутреннее хранилище, попробуйте использовать точный путь хранения:

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

или дополнительно изменить имя файла в отладчике и просто вызвать "новый файл (blah).exists()" в каждом файле, чтобы быстро узнать, какое точное имя является вашим файлом.

это также может быть реальной проблемой реализации устройства, характерной для вашего устройства. вы пробовали использовать другие устройства/эмулятор?