Content com alphainventor filemanager fileprovider root storage emulated 0 download shared text txt

Только для Android 8 и 9.

У меня здесь есть PDF-файл —

String url = "file:///storage/emulated/0/Android/data/com.verna.poc/files/Download/mypdf.pdf";

Я пытаюсь открыть этот файл для просмотра с помощью этого —

File file= new File(url);
file.setReadable(true, false);

Intent intent = new Intent(Intent.ACTION_VIEW);
Uri pdfUri = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", file);
intent.setDataAndType(pdfUri, "application/pdf");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

List<ResolveInfo> resInfoList = getApplicationContext().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    for (ResolveInfo resolveInfo : resInfoList) {
        String packageName = resolveInfo.activityInfo.packageName;
        getApplicationContext().grantUriPermission(packageName, pdfUri,
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }

Intent in = Intent.createChooser(intent, "Open File");
startActivity(in);

Открывается опция выбора файла, и когда я открываю файл с помощью программы чтения PDF-файлов Google, программа чтения PDF-файлов открывается и закрывается немедленно. Что не так в моем коде?

Я не могу открыть ни один файл из папки загрузки.

   DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
    request.setDescription(descricao);
    request.setTitle(titulo);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        request.allowScanningByMediaScanner();
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    }
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, nome);

    enq = downloadManager.enqueue(request);

И я пытаюсь открыть этот файл вот так:

downloadManager = (DownloadManager)getContext().getSystemService(DOWNLOAD_SERVICE);
        BroadcastReceiver receiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                if(DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
                    long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
                    DownloadManager.Query query = new DownloadManager.Query();
                    query.setFilterById(enq);
                    Cursor c = downloadManager.query(query);
                    if(c.moveToFirst()) {
                        int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
                        if(DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                            String uriString = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));

                            if (uriString.substring(0, 7).matches("file://")) {
                                uriString =  uriString.substring(7);
                            }

                            File file = new File(uriString);

                            Uri uriFile = FileProvider.getUriForFile(getContext(), BuildConfig.APPLICATION_ID + ".fileprovider", file);
                            String mimetype = "application/pdf";
                            Intent myIntent = new Intent(Intent.ACTION_VIEW);
                            myIntent.setDataAndType(uriFile, mimetype);

                            Intent intentChooser = Intent.createChooser(myIntent, "Choose Pdf Application");
                            startActivity(intentChooser);
                        }
                    }
                }
            }
        };

        getContext().registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

Я объявляю своего поставщика файлов в манифесте следующим образом:

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>

И с этим:

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

Но когда я нажимаю кнопку загрузки, я получаю следующее сообщение: «Этот файл может быть недоступен. Проверьте местоположение или сеть и повторите попытку».

1 — файл загружается и сохраняется в папке каталога.

2 — намерение запущено, но файл не открывается.

3 — В режиме отладки дайте мне это в «новом файле (urlString)»: «urlString = / storage / emulated / 0 / Download / name.pdf»

«uriFile = content: //com.example.android.parlamentaresapp.fileprovider/Download/name.pdf»

я должен исправить наше приложение для Android N из-за изменений FileProvider. Я в основном прочитал все об этой теме для последнего нашего, но никакое решение не найдено для меня.

вот наш предыдущий код, который запускает загрузки из нашего приложения, хранит их в Download папка и вызывает ACTION_VIEW намерение как soons как DownloadManager говорит, что он закончил загрузку:

BroadcastReceiver onComplete = new BroadcastReceiver() {
    public void onReceive(Context ctxt, Intent intent) {
        Log.d(TAG, "Download commplete");

        // Check for our download
        long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        if (mDownloadReference == referenceId) {
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(mDownloadReference);
            Cursor c = mDownloadManager.query(query);
            if (c.moveToFirst()) {
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
                if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                    String localUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                    String fileExtension = MimeTypeMap.getFileExtensionFromUrl(localUri);
                    String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);

                    if (mimeType != null) {
                        Intent openFileIntent = new Intent(Intent.ACTION_VIEW);
                        openFileIntent.setDataAndTypeAndNormalize(Uri.parse(localUri), mimeType);
                        openFileIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

                        try {
                            mAcme.startActivity(openFileIntent);
                        }
                        catch (ActivityNotFoundException e) {
                            // Ignore if no activity was found.
                        }
                    }
                }
            }
        }
    }
};

это работает на Android M, но ломается на N из-за популярного FileUriExposedException. Я сейчас пытался исправить это с помощью FileProvider, но я не могу заставить его работать. Он ломается, когда я пытаюсь получить URI содержимого:

Failed to find configured root that contains /file:/storage/emulated/0/Download/test.pdf

на localUri вернулся из DownloadManager для файл:

на Environment.getExternalStorageDirectory() возвращает /storage/emulated/0 а это код для преобразования:

File file = new File(localUri);
Log.d(TAG, localUri + " - " + Environment.getExternalStorageDirectory());
Uri contentUri = FileProvider.getUriForFile(ctxt, "my.file.provider", file);
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="my.file.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
    </provider>
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_path" path="." />
</paths>

и я пробовал все значения, которые мог найдите в этом xml-файле. 🙁

ответов


if (localUri.substring(0, 7).matches("file://")) {
    localUri =  localUri.substring(7);
}
File file = new File(localUri);

здесь изменение в AndroidManifest.в XML

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"/>
</provider>

изменить путь к файлу

Uri contentUri;
if(Build.VERSION.SDK_INT == 24){
         contentUri = FileProvider.getUriForFile(MainActivity.this,
              getApplicationContext().getPackageName() + ".provider",
                    file);
 } else{
         contentUri = Uri.fromFile(file);
        }

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

чтобы уведомить Пользователя дополнительно, я показываю тост, когда загрузка будет завершена.

DownloadManager mManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);

String url = "your URL";
String filename = "file.pdf";

// Set up the request.
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url))
            .setTitle("Test")
            .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename)
            .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
            .setDescription("Downloading...")
            .setMimeType("application/pdf");

request.allowScanningByMediaScanner();
mManager.enqueue(request);
public class DownloadReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        switch (intent.getAction()) {
            case DownloadManager.ACTION_DOWNLOAD_COMPLETE:
                Toast.makeText(context, "Download completed", Toast.LENGTH_SHORT).show();
                break;
        }
    }

}

может быть это более четкое решение ?

Uri uriParser = Uri.parse(downloadedPackageUriString);
File downloadedFile = new File(uriParser.getPath());

файл:///хранение/эмуляция / 0 / загрузка / ZS%20Gac%C3%ADkov%C3%A1.формат PDF

/ хранение / эмуляция/0/загрузка / ZS Gacíková.формат PDF


Я ищу способ правильно поделиться (не открывать) внутренним файлом с внешним приложением с помощью библиотеки поддержки Android FileProvider.

следуя примеру в документах,

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.example.android.supportv4.my_files"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/my_paths" />
</provider>
ShareCompat.IntentBuilder.from(activity)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

не работает, так как FLAG_GRANT_READ_URI_PERMISSION предоставляет разрешение только для Uri, указанного в data намерения, а не стоимость EXTRA_STREAM extra (как было установлено setStream).

Я попытался скомпрометировать безопасность, установив android:exported to true для провайдера, но FileProvider внутренне проверяет, экспортируется ли он сам, когда это так, он создает исключение.

ответов


используя FileProvider из библиотеки поддержки вы должны вручную предоставить и отозвать разрешения (во время выполнения) для других приложений для чтения определенного Uri. Использовать контексте.grantUriPermission и контексте.revokeUriPermission методы.

//grant permision for app with package "packegeName", eg. before starting other app via intent
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

//revoke permisions
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

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

//grant permisions for all apps that can handle given intent
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
...
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

альтернативный метод согласно документация:

  • поместите URI содержимого в намерение, вызвав setData ().
  • далее вызовите метод Intent.setFlags() С либо FLAG_GRANT_READ_URI_PERMISSION или FLAG_GRANT_WRITE_URI_PERMISSION
    или обоих.
  • наконец, отправить намерение в другое приложение. Чаще всего вы делаете это, вызывая setResult().

    разрешения, предоставленные в намерении, остаются в силе, пока стек
    принимающая деятельность активна. Когда стек заканчивается,
    разрешения автоматически удаляются. Разрешения, предоставленные одному
    Активность в клиентском приложении автоматически распространяется на другие
    компоненты этого приложения.

кстати. если вам нужно, вы можете скопировать источник FileProvider, и attachInfo метод для предотвращения поставщика от проверки, если он экспортируется.


Это решение работает для меня с OS 4.4. Чтобы он работал на всех устройствах, я добавил обходной путь для старых устройств. Это гарантирует, что всегда используется самое безопасное решение.

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.package.name.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
<paths>
    <files-path name="app_directory" path="directory/"/>
</paths>
public static void sendFile(Context context) {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("text/plain");
    String dirpath = context.getFilesDir() + File.separator + "directory";
    File file = new File(dirpath + File.separator + "file.txt");
    Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);
    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    // Workaround for Android bug.
    // grantUriPermission also needed for KITKAT,
    // see https://code.google.com/p/android/issues/detail?id=76683
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            context.grantUriPermission(packageName, attachmentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }
    if (intent.resolveActivity(context.getPackageManager()) != null) {
        context.startActivity(intent);
    }
}

public static void revokeFileReadPermission(Context context) {
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        String dirpath = context.getFilesDir() + File.separator + "directory";
        File file = new File(dirpath + File.separator + "file.txt");
        Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);
        context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
}

разрешение аннулируется с помощью revokeFileReadPermission () в методах onResume и onDestroy () фрагмента или действия.


полностью рабочий пример кода, как обмениваться файлами из внутренней папки приложения. Протестировано на Android 7 и Android 5.

</application>
   ....
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="android.getqardio.com.gmslocationtest"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
</application>

xml / provider_paths

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <files-path
        name="share"
        path="external_files"/>
</paths>
    File imagePath = new File(getFilesDir(), "external_files");
    imagePath.mkdir();
    File imageFile = new File(imagePath.getPath(), "test.jpg");

    // Write data in your file

    Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile);

    Intent intent = ShareCompat.IntentBuilder.from(this)
                .setStream(uri) // uri from FileProvider
                .setType("text/html")
                .getIntent()
                .setAction(Intent.ACTION_VIEW) //Change if needed
                .setDataAndType(uri, "image/*")
                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

   startActivity(intent);

В моем приложении FileProvider работал из коробки, чтобы обмениваться файлами с помощью доли намерения. Не было никакой специальной конфигурации или кода, необходимого, кроме того, чтобы настроить FileProvider. В моем манифесте.xml я разместил:

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.my.apps.package.files"
        android:exported="false"
        android:grantUriPermissions="true" >
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/my_paths" />
    </provider>

В my_paths.xml у меня есть:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="files" path="." />
</paths>

в моем коде I есть:

    Intent shareIntent = new Intent();
    shareIntent.setAction(Intent.ACTION_SEND);
    shareIntent.setType("application/xml");

    Uri uri = FileProvider.getUriForFile(this, "com.my.apps.package.files", fileToShare);
    shareIntent.putExtra(Intent.EXTRA_STREAM, uri);

    startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.share_file)));

и я могу поделиться своими файлами в моем личном хранилище приложений с приложениями, такими как Gmail и google drive без каких-либо проблем.


насколько я могу сказать, это будет работать только на новых версиях Android, поэтому вам, вероятно, придется придумать другой способ сделать это. Это решение работает для меня на 4.4, но не на 4.0 или 2.3.3, поэтому это не будет полезным способом обмена контентом для приложения, которое предназначено для работы на любом устройстве Android.

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.mydomain.myapp.SharingActivity"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

обратите внимание на то, как вы указываете власти. Необходимо указать действие, из которого будет создаваться URI и запустить намерение поделиться, в этом случае действие называется SharingActivity. Это требование не очевидно из документов Google!

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

будьте осторожны, как вы укажите путь. Выше по умолчанию в корень внутренней памяти.

Uri contentUri = FileProvider.getUriForFile(getActivity(),
"com.mydomain.myapp.SharingActivity", myFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setType("image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(shareIntent, "Share with"));

в этом примере мы разделяем изображение JPEG.

Дополнительно:  Исправить ошибку BSOD Ndu.sys в Windows 10 - zanz

наконец, это, вероятно, хорошая идея, чтобы убедиться, что у вас есть сохраненный файл правильно и что вы можете получить к нему доступ с чем-то вроде этого:

File myFile = getActivity().getFileStreamPath("mySavedImage.jpeg");
if(myFile != null){
    Log.d(TAG, "File found, file description: "+myFile.toString());
}else{
    Log.w(TAG, "File not found!");
}

В моем приложении FileProvider работает просто отлично,и я могу прикреплять внутренние файлы, хранящиеся в каталоге файлов, к почтовым клиентам, таким как Gmail, Yahoo и т. д.

в моем манифесте, как указано в документации Android, я разместил:

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.package.name.fileprovider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/filepaths" />
    </provider>

и поскольку мои файлы были сохранены в корневом каталоге файлов, пути к файлам.xml были следующие:

 <paths>
<files-path path="." name="name" />

Теперь в коде:

 File file=new File(context.getFilesDir(),"test.txt");

 Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);

 shareIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
                                     "Test");

 shareIntent.setType("text/plain");

 shareIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
                                 new String[] {"email-address you want to send the file to"});

   Uri uri = FileProvider.getUriForFile(context,"com.package.name.fileprovider",
                                                   file);

                ArrayList<Uri> uris = new ArrayList<Uri>();
                uris.add(uri);

                shareIntent .putParcelableArrayListExtra(Intent.EXTRA_STREAM,
                                                        uris);


                try {
                   context.startActivity(Intent.createChooser(shareIntent , "Email:").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));                                                      


                }
                catch(ActivityNotFoundException e) {
                    Toast.makeText(context,
                                   "Sorry No email Application was found",
                                   Toast.LENGTH_SHORT).show();
                }
            }

это работает для меня.Надеюсь, это поможет:)


просто для улучшения ответа, приведенного выше:
если вы получаете NullPointerEx:

вы также можете использовать getApplicationContext() без контекста

                List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
                for (ResolveInfo resolveInfo : resInfoList) {
                    String packageName = resolveInfo.activityInfo.packageName;
                    grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
                }

Если вы получаете изображение с камеры ни одно из этих решений для Android 4.4. В этом случае лучше проверить версии.

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intent.resolveActivity(getContext().getPackageManager()) != null) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        uri = Uri.fromFile(file);
    } else {
        uri = FileProvider.getUriForFile(getContext(), getContext().getPackageName() + ".provider", file);
    }
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(intent, CAMERA_REQUEST);
}

Я хочу поделиться тем, что заблокировало нас на пару дней: код fileprovider должны вставить между тегами приложения, а не после него.
Это может быть тривиально, но это никогда не уточняется, и я думал, что мог бы помочь кому-то!
(еще раз спасибо piolo94)


андроид.ОС.FileUriExposedException: файл: / / / хранилище / эмулированный/0 / тест.txt подвергается за пределами приложения через намерение.метод GetData()

приложение сбой, когда я пытаюсь открыть файл. Он работает ниже Android нуга, но на Android нуга он падает. Это происходит только при попытке открыть файл с SD-карты, а не из системного раздела. Какие-то проблемы с разрешением?

File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line

android.ОС.FileUriExposedException:
файл: / / / хранение / эмуляция / 0 / тест.txt подвергается за пределами приложения через
Намерение.getData ()

при таргетинге на Android нуга,file:// URI больше не разрешены. Мы должны использовать content:// URIs вместо этого. Однако мое приложение должно открывать файлы в корневых каталогах. Есть идеи?

ответов


если targetSdkVersion >= 24, тогда мы должны использовать FileProvider класс, чтобы дать доступ к определенному файлу или папке, чтобы сделать их доступными для других приложений. Мы создаем собственный класс, наследующий FileProvider чтобы убедиться, что наш FileProvider не конфликтует с FileProviders, объявленными в импортированных зависимостях, как описано здесь.

шаги для замены file:// URI с content:// URI-код:

  • добавить расширение класса FileProvider

    public class GenericFileProvider extends FileProvider {}
    
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name=".GenericFileProvider"
            android:authorities="${applicationId}.my.package.name.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
  • затем создать на . Папка может потребоваться для создания, если она не существует. Содержимое файла показано ниже. Он описывает, что мы хотел бы поделиться доступом к внешнему хранилищу в корневой папке (path=".") на имя external_files.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>
  • последним шагом является изменение строки кода ниже в

    Uri photoURI = Uri.fromFile(createImageFile());
    
    Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());
    
  • Edit: если вы используете намерение заставить систему открыть ваш файл, вам может потребоваться добавить следующую строку код:

    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    

смотрите, пожалуйста, полный код и решение было объяснено здесь.


кроме решения с помощью FileProvider есть другой способ обойти эту. Проще говоря

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

на Application.onCreate(). Таким образом, виртуальная машина игнорирует файл URI экспозицию.

builder.detectFileUriExposure()

включает проверку экспозиции файла, которая также является поведением по умолчанию, если мы не устанавливаем VmPolicy.

я столкнулся с проблемой, что если я использую content:// URI чтобы отправить что-то, некоторые приложения просто не могут этого понять. И понижение target SDK версия не допускается. В этом случае мое решение полезно.


Если ваше приложение нацелено на API 24+, и вы все еще хотите / должны использовать file: / / intents, вы можете использовать hacky способ отключить проверку выполнения:

if(Build.VERSION.SDK_INT>=24){
   try{
      Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
      m.invoke(null);
   }catch(Exception e){
      e.printStackTrace();
   }
}

метод StrictMode.disableDeathOnFileUriExposure скрыт и задокументирован как:

/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/

проблема в том, что мое приложение не хромает, а скорее не хочет быть искалеченным с помощью content:// intents, которые не поняты многими приложениями. Например, открытие mp3-файла с содержимым: / / scheme предлагает гораздо меньше приложений, чем при открытии того же файл: схема//. Я не хочу платить за ошибки дизайна Google ограничивает функциональность моего приложения.

Google хочет, чтобы разработчики использовали схему контента, но система не готова к этому, в течение многих лет приложения были сделаны, чтобы использовать файлы не «контент», файлы могут быть отредактированы и сохранены обратно, в то время как файлы, обслуживаемые по схеме контента не может быть (не так ли?).



если targetSdkVersion выше 24, потом FileProvider используется для предоставления доступа.

создайте xml-файл (путь: res\xml) provider_paths.в XML

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

добавить провайдер на AndroidManifest.в XML

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
Uri uri = Uri.fromFile(fileImagePath);
Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);

и вы хорошо идти. Надеюсь на это. помогает.


сначала вам нужно добавить поставщика к вашему AndroidManifest

  <application
    ...>
    <activity>
    .... 
    </activity>
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.your.package.fileProvider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
  </application>

Теперь создайте файл в папке ресурсов xml (если вы используете Android studio, вы можете нажать Alt + Enter после выделения file_paths и выбрать опцию Создать ресурс xml)

далее в файле file_paths введите

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path path="Android/data/com.your.package/" name="files_root" />
  <external-path path="." name="external_storage_root" />
</paths>

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

Теперь все, что осталось, это создать намерение следующим образом:

    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1);
    String type = mime.getMimeTypeFromExtension(ext);
    try {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.package.fileProvider", newFile);
            intent.setDataAndType(contentUri, type);
        } else {
            intent.setDataAndType(Uri.fromFile(newFile), type);
        }
        startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT);
    } catch (ActivityNotFoundException anfe) {
        Toast.makeText(getContext(), "No activity found to open this attachment.", Toast.LENGTH_LONG).show();
    }

редактировать: я добавил корневую папку sd-карты в file_paths. Я тестировал этот код и он работает.


изменить provider_paths.в XML ниже

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

<external-path path="Android/data/${applicationId}/" name="files_root" />

<root-path
    name="root"
    path="/" />

</paths>

и в классе java(без изменений, так как принятый ответ только небольшой edit)

Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)

Это поможет мне исправить сбой для файлов из внешних хранилищ, надеюсь, это поможет кому — то с той же проблемой, что и у меня
🙂


использование fileProvider-это путь.
Но вы можете использовать этот простой обходной путь:

предупреждение: он будет исправлен в следующем выпуске Android —
https://issuetracker.google.com/issues/37122890#comment4

startActivity(intent);
startActivity(Intent.createChooser(intent, "Your title"));

я использовал ответ Палаша, приведенный выше, но он был несколько неполным, я должен был предоставить такое разрешение

Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", new File(path));

        List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }else {
        uri = Uri.fromFile(new File(path));
    }

    intent.setDataAndType(uri, "application/vnd.android.package-archive");

    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    startActivity(intent);

просто вставьте приведенный ниже код в activity onCreate ()

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());

он будет игнорировать экспозицию URI


Я не знаю, почему, я сделал все точно так же, как Pkosta (https://stackoverflow.com/a/38858040 ), но продолжал получать ошибку:

Я потратил часы на этот вопрос. Виновник? Котлин.

val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

intent было на самом деле параметр getIntent().addFlags вместо того, чтобы работать на моем недавно объявленном playIntent.


для загрузки pdf с сервера добавьте код ниже в свой класс обслуживания. Надеюсь, это поможет вам.

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName + ".pdf");
    intent = new Intent(Intent.ACTION_VIEW);
    //Log.e("pathOpen", file.getPath());

    Uri contentUri;
    contentUri = Uri.fromFile(file);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

    if (Build.VERSION.SDK_INT >= 24) {

        Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
        intent.setDataAndType(apkURI, "application/pdf");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    } else {

        intent.setDataAndType(contentUri, "application/pdf");
    }

и да, не забудьте добавить разрешения и поставщика в манифест.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application

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

</application>


просто вставьте приведенный ниже код в activity onCreate ()

StrictMode.VmPolicy.Builder builder = новый StrictMode.VmPolicy.Builder (); StrictMode.setVmPolicy (builder.build ());

Он будет игнорировать воздействие URI

удачи в кодировании 🙂


https://stackoverflow.com/a/38858040/395097 этот ответ является полным.

этот ответ Для — у вас уже есть приложение, которое было нацелено ниже 24, и теперь вы обновляетесь до targetSDKVersion >= 24.

В Android N изменяется только uri файла, открытый для стороннего приложения. (Не так, как мы использовали его раньше). Поэтому измените только те места, где вы разделяете путь с приложением 3rd party (камера в моем случае)

в нашем приложении мы были отправка uri в приложение камеры, в этом месте мы ожидаем, что приложение камеры будет хранить захваченное изображение.

  1. для android N мы генерируем новый контент: / / URL на основе uri, указывающий на
    файл.
  2. мы генерируем обычный путь к файлу на основе api для того же (используя более старый метод).
Дополнительно:  How to self-host a Paper Minecraft server on Windows

теперь у нас есть 2 разных URI для одного файла. #1 совместно с приложением камеры. Если намерение камеры-успех, мы можем получить доступ к изображению из #2.

надеюсь, что это помогает.


в моем случае я избавился от исключения, заменив SetDataAndType с SetData.


Примечание: путь xml / provider_paths.в XML (.axml) не удалось решить, даже после создания xml под ресурсы (возможно, его можно поместить в существующее место, например значения, не пробовал), поэтому я прибегнул к этому, который работает сейчас. Тестирование показало, что его нужно вызывать только один раз за запуск приложения (что имеет смысл в том, что он изменяет рабочее состояние host VM).

Примечание: xml должен быть капитализирован, так что ресурсы / Xml / provider_paths.в XML

Java.Lang.ClassLoader cl = _this.Context.ClassLoader;
Java.Lang.Class strictMode = cl.LoadClass("android.os.StrictMode");                
System.IntPtr ptrStrictMode = JNIEnv.FindClass("android/os/StrictMode");
var method = JNIEnv.GetStaticMethodID(ptrStrictMode, "disableDeathOnFileUriExposure", "()V");                
JNIEnv.CallStaticVoidMethod(strictMode.Handle, method);

4 ответа

Вы пытаетесь поделиться файлом из внутреннего хранилища с другим приложением. Чтобы это работало, вам нужно будет создать поставщика файлов. Вам нужно будет указать каталог, для которого вы хотите, чтобы поставщик файлов сгенерировал Uris.

FileProvider — это специальный подкласс ContentProvider, который обеспечивает безопасный общий доступ к файлам, связанным с приложением, создавая content: // Uri для файла вместо file: /// Uri.

FileProvider может генерировать URI содержимого только для файлов в каталогах, которые вы укажете заранее. Чтобы указать каталог, укажите его область хранения и путь в XML, используя дочерние элементы элемента. Например, следующий элемент paths сообщает FileProvider, что вы собираетесь запросить URI контента для подкаталога images / вашей частной файловой области.

В этом ответе есть хороший пример

ссылка на сайт

Вот страница документа для FileProvider

20 Июн 2020 в 12:12

У меня проблема. Когда вы создаете новый файл, используя, File file = new File (path), не добавляйте file:// перед путем.

Это правильно —

String url = "/storage/emulated/0/Android/data/com.verna.poc/files/Download/mypdf.pdf";
File file= new File(url);

Это не правильно —

String url = "file:///storage/emulated/0/Android/data/com.verna.poc/files/Download/mypdf.pdf";
File file= new File(url);

2 Мар 2019 в 09:43

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

Я использую метод ниже, чтобы сохранить файл во внутреннем хранилище:

private void savePDFtoInternalStorage(byte[] pdfAsBytes){
    //Save in internal memo cache
    File directory = mFragmentActivity.getFilesDir();
    //updating path for pdf to match with file_path.xml
    mCardStmtFile = new File(directory.getAbsolutePath(), "sample.pdf");
    OutputStream outputStream = null;
    try {
        PbLogger.e(TAG, "writing to mStmtFile");
        outputStream = new FileOutputStream(mCardStmtFile, false);
        outputStream.write(pdfAsBytes);
        outputStream.flush();
        outputStream.close();

    } catch (IOException e) {
        e.printStackTrace();

    }
}

PFB провайдер файлов объявил file_path.xml:

  <?xml version="1.0" encoding="utf-8"?>
   <paths>
      <files-path path="/" name="secretpdf" />
   </paths>

PFB — запись манифеста Android для поставщика файлов:

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.***.********.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <!-- ressource file to create -->
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths">
        </meta-data>
    </provider>

Используйте приведенный ниже код для запуска PDF:

 Intent intentShareFile = new Intent(Intent.ACTION_VIEW);
        if (mStmtFile.exists()) {
            intentShareFile.setType("application/pdf");
            Uri fileUri = FileProvider.getUriForFile(
                    mFragmentActivity,
                    "com.****.********.provider",
                    mCardStmtFile);
            intentShareFile.putExtra(Intent.EXTRA_STREAM, fileUri);
            intentShareFile.putExtra(Intent.EXTRA_SUBJECT,
                    getDescription());
            //adding grant read permission to share internal file
            intentShareFile.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            startActivity(Intent.createChooser(intentShareFile, "Share File"));
        }

1 Апр 2019 в 16:32

1 Мар 2019 в 19:15

3 ответа

Папка «/storage /emulated /» на самом деле не существует. Это то, что можно назвать «символической ссылкой» или, проще говоря, ссылкой на то, где хранятся реальные данные. Вам нужно будет найти фактическое физическое местоположение на вашем устройстве, где оно хранится. Поскольку он находится в /storage/emulated/0/DCIM/.thumbnails, он, вероятно, находится в /Internal Storage /DCIM /.thumbnails /. Обратите внимание, что эта папка, вероятно, содержит только «миниатюры», которые представляют собой очень маленькие версии реальных файлов. Возможно, ваши настоящие файлы ушли навсегда, если ваша SD-карта невосстановима.

Изменить: «Никогда не говори никогда» Ссылка на самом деле относится к эмулированной SD-карте на вашем устройстве. Получите доступ к данным «напрямую» * в качестве SD-карты, и у вас будет доступ ко всем данным на этой карте. * Файл Airdroid «приложение» позволит это. И, если память обслуживается, доступ через USB-кабель для передачи данных также будет.

ответил phyrfox 3 августа 2015, 06:22:20 3

Я мог бы восстановить мои файлы, перейдя в «Настройки -> Хранение и usb -> Изображения -> Камера. Если вы выберете фотографию, то в правом верхнем меню изменится значение« Выбрать все »и« Копировать в .. »Сначала выберите все, затем скопируйте в папку« Загрузка »или в зависимости от того, что видно в проводнике. После копирования в нужную папку Скопируйте из этой папки в папку« Компьютеры ».

ответил The Analyst 12 июня 2016, 17:28:39 1

У меня была аналогичная проблема, только папка не начиналась с . . Что для меня решило сбросить Media Manager.

Перейдите в Settings -> Apps , убедитесь, что Show System проверяется в меню, ищите Media Store или что-то в этом роде, щелкните по нему и Clear Data . Затем перезагрузите телефон и подождите, пока Media Store не восстановит себя.

Перестройка занимает несколько минут. Вы можете проверить, выполняется ли это путем мониторинга использования данных в том же месте, где вы его очистили. Как только он перестает расти в течение пары минут, это делается. Например, мой остановился на 21 МБ.

ответил André Chalella 6 марта 2017, 05:09:28

Похожие вопросы

8 Как получить доступ к столбцу, имя которого я не могу получить доступ в цепочечных операций 3 Как я могу удалить учетную запись Yahoo навсегда, когда я не могу получить к ней доступ? 4 Как я могу избежать состояния «TypeError: не могу получить доступ к мертвому объекту» в моем дополнении Firefox? 24 Как я могу получить доступ к google.com за пределами США? 23 Могу ли я получить доступ к Gmail через SSL? 14 Я не могу получить доступ к корневой папке 39 Как я могу получить доступ к ServletContext из веб-службы JAX-WS? 9 Как я могу получить доступ к переменным JavaScript в JSP? 2 Не могу получить доступ к личным файлам 78 Как я могу получить доступ к сессии в веб-методе? 59 Как я могу получить доступ к ResourceDictionary в wpf из кода C #? 7 Как я могу получить доступ к SQLite с помощью C #? 4 Как я могу получить доступ к `this` в обработчике событий? 4 Не могу получить доступ к JPanel публично () 4 Как я могу получить доступ к фейсбуку на питоне? 4 Как я могу получить доступ к модифицированному QLabel? 4 Как я могу получить доступ к иконкам GitHub? 2 Magento — не удается получить доступ к удаленному сайту, но может получить доступ к простому файлу index.html 7 Не удается получить доступ к локальному хосту после обновления до Mavericks, но может получить доступ к 127.0.0.1 107 Могу ли я получить доступ к Ubuntu из Windows удаленно?

Популярные теги

security × 330 linux × 316 macos × 282 7 × 268 performance × 244 command-line × 241 sql-server × 235 joomla-3.x × 222 java × 189 c++ × 186 windows × 180 cisco × 168 bash × 158 c# × 142 gmail × 139 arduino-uno × 139 javascript × 134 ssh × 133 seo × 132 mysql × 132 19.04.2015

Content com alphainventor filemanager fileprovider root storage emulated 0 download shared text txt

После этого скачиваем интересующий вас файл из Интернета. Как правило, его можно открыть, просто нажав на уведомление, однако если по какой-либо причине оно свернулось, а файл открыть всё же нужно, на помощь придет файловый менеджер. Поможет он и просто в том случае, если кто-то из ваших друзей перекинул документ на ваш смартфон с помощью USB-кабеля. Тогда вопрос «Где же он находится?» станет самым актуальным.

Все скачанные из Интернета файлы хранятся в папке «Download». Для её открытия вам требуется открыть файловый менеджер, затем найти в каталогах данную папку. После этого вы сможете открыть любой скачанный ранее файл, в том числе установочный *.apk, *.zip и так далее.

В случае, если вам неизвестно, где именно сохранился ваш файл, вы можете произвести поиск, нажав на иконку лупы в приложении, речь не идет именно о Cabinet. С тем же успехом можно использовать и ES Проводник, например.

Хотим обезопасить пользователей от случайных действий. Так как память смартфонов разделена на два раздела: системная и внутренняя, есть риски удаления системных файлов, что приведет к некорректной работе смартфона. Для их удаления вам потребуются Root-права, однако возможны и случаи, когда Root-доступ имеется, а сам пользователь об этом и понятия не имеет.

Таким образом, мы научились находить скачанные из Интернета файлы. Предлагаем поддержать неопытных пользователей и прокомментировать статью, добавив свои дополнения.

Как найти папку storage на Андроид?

Опишите причину своей жалобы

Распаковал архив в storage и не могу найти указаный путь

как найти папку /storage/sdcard0/ на Android?

Это вообще-то не папка, storage это хранилище общее, включает в себя память устройства, внутреннюю память и внешнюю карту памяти.Потому и пишет storage/SDcard 0/а дальше уже папки. Вы распаковать наверное в sdcard 0, там и надо искать, а может в sdcard1 смотря куда направили.

Для того, чтобы было все понятно с этим, достаточно установить любой файловый менеджер, например Тотал Коммандер, а вот в нем уже можно увидеть, что папка storage/sdcard0 находится вот здесь:

Дополнительно:  Проверка компьютера на ошибки | Компьютер для чайников

sdcard0 — обычно карта памяти устройства, во всяком случае у меня именно так;

sdcard1 — встроенная память, на которой можно хранить файлы(для приложений имеется другой отдел памяти, но в данном случае это не важно).

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

На самом деле storage в android это не папка а раздел который включает в себя реальные карты памяти, как внутреннюю так и флеш память (SD). А когда прописывается путь к какому то файлу то в начале указывается storage.

Недавно я потерял содержимое карты micro-SD и не обновил содержимое достаточно недавно. К счастью, я заметил, что фотографии, которые я отсутствовал, оказались в /storage/emulated/0/DCIM/.thumbnails на моем устройстве Android 5.1. Печально то, что я не могу добраться до этих файлов, чтобы что-либо делать с ними на моем устройстве. Единственный способ, которым я даже знал, что они были там, — это то, что ASTRO File Manager вывел их, когда искал фотографии. Мне бы хотелось, чтобы они были в надежном месте и надеялись сделать это через мой компьютер. Кто-нибудь знает, как заставить мой компьютер (Windows 7) распознать этот файл? (Естественно, я сказал своему компьютеру просмотреть скрытые файлы, так что это не проблема.) Все, что я вижу, это /Внутреннее хранилище и /SD-карта (нет /хранилище). Если у вас есть обходной путь, сообщите мне об этом. Если я смогу переместить (или еще лучше скопировать) эти фотографии в другое место на своем телефоне, а затем переместить их, это будет совершенно замечательно.

Сохранение файла в / Download дает имя / storage / emulated / 0 / Download. Иногда файл недоступен

На своих устройствах Samsung я помещаю файл в папку /Download . Когда я выбираю файл в своем приложении (через файловый браузер), файл называется /storage/emulated/0/Download/myfile.db . Это имя хранится в настройках моего приложения.

При запуске приложения файл должен быть открыт. Сначала приложение проверяет, существует ли файл. В некоторых случаях, скажем, раз в 2 недели, файл считается несуществующим! Это происходит и у моих друзей на мобильном телефоне с полугода.

Почему так происходит? Годами все шло гладко.

Этот код работает 6 лет. Конечно, я добавил разрешения.

Ваша помощь очень ценится!

1 ответ

Вы не можете использовать Загрузку в качестве гарантированного места для хранения файлов. Для приложения следует использовать внутреннее хранилище. Вот что Google написал о папке загрузки:

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

Вот дополнительная информация о папке Загрузить от Google.

Внимание! Внешнее хранилище может стать недоступным, если пользователь извлечет SD-карту или подключит устройство к компьютеру. И файлы по-прежнему видны пользователю и другим приложениям, имеющим разрешение READ_EXTERNAL_STORAGE. Поэтому, если функциональность вашего приложения зависит от этих файлов или вам нужно полностью ограничить доступ, вам следует вместо этого записывать файлы во внутреннее хранилище .

Как я могу получить доступ к /storage /emulated /0 /DCIM на устройстве Android?

Скачивая файлы на своем Андроид смартфоне или планшете, многие пользователи сталкиваются с тем, что они не могут найти скачанный файл. Они просто не знают, куда скачиваются файлы на Андроид. В этом материале мы постараемся решить эту проблему.

Если коротко, то на Андроид файлы скачиваются в папку Download. А место расположения этой папки может меняться в зависимости от прошивки вашего Андроид устройства. Например, папка Download может размещаться по одному из адресов:

Для того чтобы найти данную папку установите хороший файловый менеджер. Например, такой как AntTek Explorer Ex.

Content com alphainventor filemanager fileprovider root storage emulated 0 download shared text txt

Если скачанных файлов нет в папке Download, то возможно программа, с помощью которой вы скачивали, сохранила файлы в другую папку. Откройте программу и войдите в ее настройки. Возможно, там будет указана папка для сохранения скачанных файлов. Например, в веб-браузере Dolphin для того чтобы сменить папку для сохранения скачанных файлов необходимо открыть: Настройки – Конфиденциальность и персональные данные – Загрузить директорию.

Кроме этого, нередко в самой программе можно посмотреть список файлов, которые были скачаны. Например, такая функция есть в веб-браузерах Dolphin и Firefox.

Content com alphainventor filemanager fileprovider root storage emulated 0 download shared text txt

Также нужно помнить, что после каждого скачанного файла в верхнем меню с уведомлениями появляется специальное сообщение, о том, что был скачан файл. Нажав на это сообщение, вы сможете открыть скачанный файл.

Content com alphainventor filemanager fileprovider root storage emulated 0 download shared text txt

Есть чем дополнить материал? Расскажите об этом в комментариях.

—> Разрабатывая приложение для проведения соревнований, я столкнулся с проблемой хранения базы данных. Проблема состояла в том, как мне определить внешнюю карту памяти. В целом поиск в сети точного ответа не дал. Поэтому, объединив все найденные результаты, я собрал свой класс. Если кому интересно, смотрим под катом. Итак, начнем с теории.

Терминология

Гугл нам говорит, что есть следующие понятия:

  1. Внутренняя (internal) память — это часть встроенной в телефон карты памяти. При ее использовании по умолчанию папка приложения защищена от доступа других приложений (Using the Internal Storage).
  2. Внешняя (external) память — это общее «внешнее хранилище», т.е. это может быть как часть встроенной памяти, так и удаляемое устройство. Обычно это часть встроенной памяти, как удаляемое устройство я видел в последний раз на андройде 2.2, где встроенная память была около 2Гб, и подключаемая память становилась внешней (Using the External Storage).
  3. Удаляемая (removable) память — все хранилища, которые могут быть удалены из устройства без «хирургических» вмешательств.

Результаты поиска

Чтобы ответить на поставленный вопрос я обратился к всезнающему гуглу. Но и он мне не дал четкого ответа. Было рассмотрено множество вариантов определения от использования стандартных функций, которые ведут к внешней памяти, но ничего общего с удаляемыми устройствами хранения данных они не имеют, до обработки правил монтирования устройств (Android же на ядре Linux работает). В последних случаях были использованы «зашитые» пути к папке с примонтироваными устройствами (в различных версиях эта директория разная). Не стоит забывать, что от версии к версии правила монтирования меняются. В конечном итоге я решил объединить все полученные знания и написал свой класс, который может нам вернуть пути к внешним и удаляемым устройствам.

Описание кода

Был создан класс MountDevice, который содержит в себе путь к устройству, тип устройства и некий хэш. Типов устройств выделено два (внутреннюю память я не стал трогать, так как к ней доступ можно получить через API системы).

И был создан класс StorageHelper, который и осуществляет поиск доступных карт памяти. В классе StorageHelper реализовано два способа поиска — через системное окружение (Environment) и с использованием утилиты Linux mount, а точнее результата ее выполнения.

Способ первый — Environment

При работе с окружением я использую стандартную функцию getExternalStorageDirectory() для получения информации о внешней памяти. Чтобы получить информацию о удаляемой памяти, я использую переменную окружения «SECONDARY_STORAGE«. Внешняя память всегда одна и обычно всегда есть, поэтому проверяем ее на читаемость, вычисляем хэш и запоминаем. Удаляемой памяти может быть много, поэтому необходимо полученную строку разбить по разделителю и проверять каждое значение. Функция fillDevicesEnvirement

Вариант решения взят со stackoverflow. Ответ где-то там внизу.

Способ второй — mount

Так как у меня долго не получалось заставить систему мне сказать путь к удаляемой памяти, я решил искать в сторону примонтированных устройств. В системе есть файлы конфигурации, в которых описаны правила монтирования внешних устройств. Все бы хорошо, но на Android версии 4.* к этому файлу простым смертным доступа нет, поэтому рассматривать этот способ не буду. Вернемся к утилите mount. При запуске без параметров команда возвращает список смонтированных файловых систем. Удаляемые устройства имеют обычно формат файловой системы FAT, то будем выделять строки, в которых есть характеристика «fat«. Внешняя память будет характеризоваться параметром «fuse«. Примечание: при использовании такого способа не всегда корректно (скорее всего я что-то не учел) определяются типы смотнтированных устройств. Разницу замечал на разных версиях Android. Поэтому этот способ можно использовать как дополнительный. Функция fillDevicesProcess

Вариант решения взят со stackoverflow. Ответов там несколько примерно одинаковых.

Про дублирование

Многие замечали в директории монтирования устройств такую картину:

И что самое интересно, все это одна и та же внешняя карта памяти. Такое дробление начинается с версии Jelly Bean и сделано это для поддержки многопользовательского режима работы системы. Более подробно тут. И вот, чтобы не получать одну и туже карту памяти как различные устройства, необходим способ определения идентичности. Если бы был доступ к конфигурации монтирования, то и вопросов не было. Но доступа нет. Поэтому я тут подсмотрел решение с расчетом хэша для каждого устройства:

  1. создаем StringBuilder
  2. записываем в него общий размер устройства и размер используемого пространства устройства
  3. обходим содержимое корня устройства
  4. записываем имя каталога
  5. записываем имя файла и размер
  6. вычисляем hash

Своя функция расчета хэша calcHash

Пример использования

Заключение

Подробные рассуждения по этому вопросу понимания памяти в Android, некоторые советы можно прочитать тут. Исходный код всего класса расположен еще нигде не расположен. На днях постараюсь разместить на gitHub. Кто еще какими способами пользуется? UPD1: Исходный код класса на bitbucket 173 237.3k 173

Оцените статью
Master Hi-technology
Добавить комментарий