Android DownloadManager API - открытие файла после загрузки?

Я столкнулся с проблемой открытия загруженного файла после успешной загрузки через DownloadManager API. В моем коде:

Uri uri=Uri.parse("http://www.nasa.gov/images/content/206402main_jsc2007e113280_hires.jpg");

Environment
    .getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
    .mkdirs();

lastDownload = mgr.enqueue(new DownloadManager.Request(uri)
    .setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI |
                            DownloadManager.Request.NETWORK_MOBILE)
    .setAllowedOverRoaming(false)
    .setTitle("app update")
    .setDescription("New version 1.1")
    .setShowRunningNotification(true)
    .setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "a.apk"));

Cursor c=mgr.query(new DownloadManager.Query().setFilterById(lastDownload));

if(c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)) == 8) {
    try {
        mgr.openDownloadedFile(c.getLong(c.getColumnIndex(DownloadManager.COLUMN_ID)));
    } catch (NumberFormatException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        Log.d("MGR", "Error");
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        Log.d("MGR", "Error");
    }
}

Проблема в том, когда вызывается if(c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))==8). Получил статус -1 и исключение. Есть ли лучший способ открыть загруженные файлы с помощью DownloadManager API? В моем примере я загружаю большое изображение, в реальной ситуации я загружаю APK файл, и мне нужно сразу после udpate отобразить диалоговое окно установки.

Изменить: я понял, что статус = 8 после успешной загрузки. У вас может быть другой подход "проверка успешной загрузки"

Спасибо


person Waypoint    schedule 30.08.2011    source источник


Ответы (4)


Вам необходимо зарегистрировать приемник, когда загрузка будет завершена:

registerReceiver(onComplete, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

и обработчик BroadcastReciever

BroadcastReceiver onComplete=new BroadcastReceiver() {
    public void onReceive(Context ctxt, Intent intent) {
        // Do Something
    }
};

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

РЕДАКТИРОВАТЬ:

В качестве предложения я бы пока не рекомендовал использовать API 9: http://developer.android.com/resources/dashboard/platform-versions.html

Есть способы обойти это, создав свой собственный обработчик загрузки, как это сделал я, потому что мы не хотели отчуждать большую часть пользовательской базы нашего Android, для этого вам потребуется: Создать AsyncTask, который обрабатывает загрузку файла.

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

и тогда вам нужно будет обработать открытие файла:

protected void openFile(String fileName) {
    Intent install = new Intent(Intent.ACTION_VIEW);
    install.setDataAndType(Uri.fromFile(new File(fileName)),
            "MIME-TYPE");
    startActivity(install);
}
person Itai Sagi    schedule 30.08.2011
comment
Спасибо, я переместил файл mgr.openDownloadedFile на приемник вещания, но все равно у меня такие же проблемы :-( - person Waypoint; 30.08.2011
comment
Смотрите мою правку, вам понадобится в BroadcastReciever, чтобы иметь какой-то метод для его открытия - например, метод openFile (), который я разместил здесь. - person Itai Sagi; 30.08.2011
comment
Спасибо, у меня есть mady acync task downloader, но компания хочет иметь готовое решение DownloadManager, стоящее бок о бок с решением для загрузки. - person Waypoint; 30.08.2011
comment
Я использую стандартный mime-тип для установки пакетов application / vnd.android.package-archive, но безуспешно :-( открывается вкладка с завершающим действием приложения: и у меня есть список возможных действий, установка не входит в их число ... - person Waypoint; 30.08.2011
comment
Вы сказали, что хотите открыть jpeg ... не устанавливать APK, рекомендуется прочитать: stackoverflow.com/questions/6121615/ и stackoverflow.com/questions/6109304/ - person Itai Sagi; 30.08.2011
comment
нет, я хочу установить APK, только в этом примере я использовал какой-то адрес изображения, но мой HTTP URI имеет * .apk в конце. Я пробовал все мыслить в этих вопросах, и я использую его то же самое, но похоже, что я не могу выполнить установку, даже код правильный .... - person Waypoint; 30.08.2011
comment
какую ошибку выдает DDMS? или вы получаете просто необработанное исключение? - person Itai Sagi; 30.08.2011
comment
@Hmyzak позвольте нам продолжить обсуждение в чате - person Itai Sagi; 30.08.2011

Проблема

Android DownloadManager API - открытие файла после загрузки?

Решение

/**
 * Used to download the file from url.
 * <p/>
 * 1. Download the file using Download Manager.
 *
 * @param url      Url.
 * @param fileName File Name.
 */
public void downloadFile(final Activity activity, final String url, final String fileName) {
    try {
        if (url != null && !url.isEmpty()) {
            Uri uri = Uri.parse(url);
            activity.registerReceiver(attachmentDownloadCompleteReceive, new IntentFilter(
                    DownloadManager.ACTION_DOWNLOAD_COMPLETE));

            DownloadManager.Request request = new DownloadManager.Request(uri);
            request.setMimeType(getMimeType(uri.toString()));
            request.setTitle(fileName);
            request.setDescription("Downloading attachment..");
            request.allowScanningByMediaScanner();
            request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
            request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);
            DownloadManager dm = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
            dm.enqueue(request);
        }
    } catch (IllegalStateException e) {
        Toast.makeText(activity, "Please insert an SD card to download file", Toast.LENGTH_SHORT).show();
    }
}

/**
 * Used to get MimeType from url.
 *
 * @param url Url.
 * @return Mime Type for the given url.
 */
private String getMimeType(String url) {
    String type = null;
    String extension = MimeTypeMap.getFileExtensionFromUrl(url);
    if (extension != null) {
        MimeTypeMap mime = MimeTypeMap.getSingleton();
        type = mime.getMimeTypeFromExtension(extension);
    }
    return type;
}

/**
 * Attachment download complete receiver.
 * <p/>
 * 1. Receiver gets called once attachment download completed.
 * 2. Open the downloaded file.
 */
BroadcastReceiver attachmentDownloadCompleteReceive = 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);
            openDownloadedAttachment(context, downloadId);
        }
    }
};

/**
 * Used to open the downloaded attachment.
 *
 * @param context    Content.
 * @param downloadId Id of the downloaded file to open.
 */
private void openDownloadedAttachment(final Context context, final long downloadId) {
    DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
    DownloadManager.Query query = new DownloadManager.Query();
    query.setFilterById(downloadId);
    Cursor cursor = downloadManager.query(query);
    if (cursor.moveToFirst()) {
        int downloadStatus = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS));
        String downloadLocalUri = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
        String downloadMimeType = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_MEDIA_TYPE));
        if ((downloadStatus == DownloadManager.STATUS_SUCCESSFUL) && downloadLocalUri != null) {
            openDownloadedAttachment(context, Uri.parse(downloadLocalUri), downloadMimeType);
        }
    }
    cursor.close();
}

/**
 * Used to open the downloaded attachment.
 * <p/>
 * 1. Fire intent to open download file using external application.
 *
 * 2. Note:
 * 2.a. We can't share fileUri directly to other application (because we will get FileUriExposedException from Android7.0).
 * 2.b. Hence we can only share content uri with other application.
 * 2.c. We must have declared FileProvider in manifest.
 * 2.c. Refer - https://developer.android.com/reference/android/support/v4/content/FileProvider.html
 *
 * @param context            Context.
 * @param attachmentUri      Uri of the downloaded attachment to be opened.
 * @param attachmentMimeType MimeType of the downloaded attachment.
 */
private void openDownloadedAttachment(final Context context, Uri attachmentUri, final String attachmentMimeType) {
    if(attachmentUri!=null) {
        // Get Content Uri.
        if (ContentResolver.SCHEME_FILE.equals(attachmentUri.getScheme())) {
            // FileUri - Convert it to contentUri.
            File file = new File(attachmentUri.getPath());
            attachmentUri = FileProvider.getUriForFile(activity, "com.freshdesk.helpdesk.provider", file);;
        }

        Intent openAttachmentIntent = new Intent(Intent.ACTION_VIEW);
        openAttachmentIntent.setDataAndType(attachmentUri, attachmentMimeType);
        openAttachmentIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        try {
            context.startActivity(openAttachmentIntent);
        } catch (ActivityNotFoundException e) {
            Toast.makeText(context, context.getString(R.string.unable_to_open_file), Toast.LENGTH_LONG).show();
        }
    }
}

Инициализировать сведения о FileProvider

Отключить FileProvider в AndroidManifest

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

Добавьте следующий файл "res -> xml -> file_path.xml"

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

Примечание

Зачем использовать FileProvider

  1. Начиная с Android 7.0 мы не можем делиться FileUri с другими приложениями.
  2. Используя "DownloadManager.COLUMN_LOCAL_URI", мы получим только FileUri, поэтому нам нужно преобразовать его в ContentUri и поделиться с другим приложением.

Проблема с использованием DownloadManager.getUriForDownloadedFile (длинный идентификатор)

  1. Не используйте «DownloadManager.getUriForDownloadedFile (длинный идентификатор)» - чтобы получить Uri из downloadId для открытия файла с помощью внешнего приложения.
  2. Поскольку из Android 6.0 и 7.0 метод getUriForDownloadedFile возвращает локальный uri (доступ к которому может получить только наше приложение), мы не можем поделиться этим Uri с другим приложением, потому что они не могут получить доступ к этому uri (но это исправлено в Android 7.1. см. здесь коммит Android).
  3. Ссылка на исходный код Android DownloadManager.java & Downloads.java
  4. Следовательно, всегда используйте столбец «DownloadManager.COLUMN_LOCAL_URI» для получения Uri.

Ссылка

  1. https://developer.android.com/reference/android/app/DownloadManager.html
  2. https://developer.android.com/reference/android/support/v4/content/FileProvider.html
person Vasanth    schedule 02.12.2016
comment
Не могли бы вы рассказать, как использовать DownloadManager.COLUMN_LOCAL_URI для получения локального URI? - person Gokul NC; 04.12.2016
comment
Неважно, это помогло: stackoverflow.com/questions/9194361/ - person Gokul NC; 04.12.2016
comment
Превосходная проработка. - person karthik kolanji; 17.07.2017
comment
Откуда com.freshdesk.helpdesk.provider? Вам нужно явно знать, с помощью какого приложения вы открываете файл? Можете ли вы открыть с помощью приложения по умолчанию? - person Sampo; 30.11.2017
comment
мы не можем поделиться этим URI с другим приложением, потому что они не могут получить доступ к этому URI. Я думаю, FLAG_GRANT_READ_URI_PERMISSION) решает эту проблему? - person charlag; 15.03.2018
comment
Разве это не должно сохранять результат dm.enqueue для сравнения в BroadcastReceiver? В противном случае я бы подумал, что это попытается открыть загрузки, запущенные другими приложениями. - person Taylor Buchanan; 10.07.2018
comment
Привет, Дхрувам, Конечно, ты можешь использовать. - person Vasanth; 08.08.2019
comment
Спас мой день. Спасибо. - person Anjula; 21.11.2019

Для Kotlin вы можете просто использовать метод URL.openStream() для чтения и сохранения файла в вашем каталоге.

Если вы хотите сделать что-то более изящное, например, фоновые потоки. Вам следует ознакомиться со статьей Эли на Medium.

https://medium.com/mobile-app-development-publication/download-file-in-android-with-kotlin-874d50bccaa2

private fun downloadVcfFile() {
    CoroutineScope(Dispatchers.IO).launch {
        val url = "https://srv-store5.gofile.io/download/JXLVFW/vcard.vcf"
        val path = "${Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)}/contacts.vcf"

        URL(url).openStream().use { input ->
            FileOutputStream(File(path)).use { output ->
                input.copyTo(output)

                val file = File(path)
                file.createNewFile()
                onMain { saveVcfFile(file) }
            }
        }
    }
}
person Morgan Koh    schedule 06.11.2020

не забудьте добавить <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> в файл AndroidMannifest.xml

person xiaoshitou    schedule 28.08.2019
comment
Зачем это нужно? - person rmtheis; 21.11.2019
comment
см. документ Android 9.0 - person xiaoshitou; 23.11.2019