使用 FileProvider 打开 Android N 上下载的文件
Open downloaded file on Android N using FileProvider
由于 FileProvider 的更改,我必须为 Android N 修复我们的应用程序。我基本上已经阅读了最后一个主题的所有内容,但没有找到适合我的解决方案。
这是我们之前的代码,它开始从我们的应用程序下载,将它们存储在 Download
文件夹中,并在 DownloadManager
告诉他完成下载后立即调用 ACTION_VIEW
意图:
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,但由于流行 FileUriExposedException
而无法用于 N。我现在尝试使用 FileProvider
来解决这个问题,但我无法让它工作。当我尝试获取内容 URI 时出现问题:
Failed to find configured root that contains /file:/storage/emulated/0/Download/test.pdf
从文件 DownloadManager
返回的 localUri
是:
file:///storage/emulated/0/Download/test.pdf
Environment.getExternalStorageDirectory()
returns /storage/emulated/0
这是转换代码:
File file = new File(localUri);
Log.d(TAG, localUri + " - " + Environment.getExternalStorageDirectory());
Uri contentUri = FileProvider.getUriForFile(ctxt, "my.file.provider", file);
来自AndroidManifest.xml
<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>
file_paths.xml
:
<?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 文件中找到的所有值。 :(
此处更改 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);
}
多亏了@greenaps,我才得以解决这个问题。从 DownlodManager
检索到的本地 URI 以 file://
为前缀。对于新的 FileProvider
:
必须删除
if (localUri.substring(0, 7).matches("file://")) {
localUri = localUri.substring(7);
}
File file = new File(localUri);
我遇到了类似的问题,解决方法是不自动打开文件而是显示 'Download complete' 通知,然后让 Android 系统在用户单击通知时打开文件。
为了另外通知用户,我在下载完成时显示 Toast。
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());
之前
file:///storage/emulated/0/Download/ZS%20Gac%C3%ADkov%C3%A1.pdf
在
之后
/storage/emulated/0/Download/ZS Gacíková.pdf
如何获取文件的确切路径文件名。越来越低的设备。
public class RealPathUtil {
public static String getRealPath(Context context, Uri fileUri) {
String realPath;
// SDK < API11
if (Build.VERSION.SDK_INT < 11) {
realPath = RealPathUtil.getRealPathFromURI_BelowAPI11(context, fileUri);
}
// SDK >= 11 && SDK < 19
else if (Build.VERSION.SDK_INT < 19) {
realPath = RealPathUtil.getRealPathFromURI_API11to18(context, fileUri);
}
// SDK > 19 (Android 4.4) and up
else {
realPath = RealPathUtil.getRealPathFromURI_API19(context, fileUri);
}
return realPath;
}
@SuppressLint("NewApi")
public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
String[] proj = {MediaStore.Images.Media.DATA};
String result = null;
CursorLoader cursorLoader = new CursorLoader(context, contentUri, proj, null, null, null);
Cursor cursor = cursorLoader.loadInBackground();
if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);
cursor.close();
}
return result;
}
public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri) {
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = 0;
String result = "";
if (cursor != null) {
column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);
cursor.close();
return result;
}
return result;
}
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @author paulburke
*/
@SuppressLint("NewApi")
public static String getRealPathFromURI_API19(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
这是精确解。只需打电话
Uri selectedResume = data.getData();
String mediaResumePath = RealPathUtil.getRealPath(this, selectedResume);
这就是我下载、保存并return一个文件到视图模型的方式:)
接口文件下载器{
伴生对象{
私有 val 实例:FileDownloader by lazy {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.HEADERS
val client = OkHttpClient.Builder()
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.callTimeout(300, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build()
val downloaderApi = Retrofit.Builder()
.baseUrl("https://exmaple.com")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(FileDownloader::class.java)
downloaderApi
}
suspend fun downloadAndSaveFile(
url: String
): File {
val file = getPdfFile()
val responseBody = try {
instance.downloadFile(url)
} catch (e: Exception) {
throw FileDownloadException(url, e)
}
@Suppress("BlockingMethodInNonBlockingContext")
try {
FileOutputStream(file).use { outputStream ->
responseBody.byteStream().use { inputStream ->
inputStream.copyTo(outputStream)
}
}
} catch (e: Exception) {
throw FileDownloadException(url, e)
}
return file
}
}
@GET
@Streaming
suspend fun downloadFile(@Url utl: String): ResponseBody
}
fun getPdfFile(): File {
File(App.appContext.filesDir, "pdfs").apply { mkdirs() }
return File(App.appContext.filesDir, "tempPDF.pdf")
}
class FileDownloadException(url: String, e: Exception) : Throwable()
由于 FileProvider 的更改,我必须为 Android N 修复我们的应用程序。我基本上已经阅读了最后一个主题的所有内容,但没有找到适合我的解决方案。
这是我们之前的代码,它开始从我们的应用程序下载,将它们存储在 Download
文件夹中,并在 DownloadManager
告诉他完成下载后立即调用 ACTION_VIEW
意图:
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,但由于流行 FileUriExposedException
而无法用于 N。我现在尝试使用 FileProvider
来解决这个问题,但我无法让它工作。当我尝试获取内容 URI 时出现问题:
Failed to find configured root that contains /file:/storage/emulated/0/Download/test.pdf
从文件 DownloadManager
返回的 localUri
是:
file:///storage/emulated/0/Download/test.pdf
Environment.getExternalStorageDirectory()
returns /storage/emulated/0
这是转换代码:
File file = new File(localUri);
Log.d(TAG, localUri + " - " + Environment.getExternalStorageDirectory());
Uri contentUri = FileProvider.getUriForFile(ctxt, "my.file.provider", file);
来自AndroidManifest.xml
<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>
file_paths.xml
:
<?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 文件中找到的所有值。 :(
此处更改 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);
}
多亏了@greenaps,我才得以解决这个问题。从 DownlodManager
检索到的本地 URI 以 file://
为前缀。对于新的 FileProvider
:
if (localUri.substring(0, 7).matches("file://")) {
localUri = localUri.substring(7);
}
File file = new File(localUri);
我遇到了类似的问题,解决方法是不自动打开文件而是显示 'Download complete' 通知,然后让 Android 系统在用户单击通知时打开文件。
为了另外通知用户,我在下载完成时显示 Toast。
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());
之前
file:///storage/emulated/0/Download/ZS%20Gac%C3%ADkov%C3%A1.pdf
在
之后/storage/emulated/0/Download/ZS Gacíková.pdf
如何获取文件的确切路径文件名。越来越低的设备。
public class RealPathUtil {
public static String getRealPath(Context context, Uri fileUri) {
String realPath;
// SDK < API11
if (Build.VERSION.SDK_INT < 11) {
realPath = RealPathUtil.getRealPathFromURI_BelowAPI11(context, fileUri);
}
// SDK >= 11 && SDK < 19
else if (Build.VERSION.SDK_INT < 19) {
realPath = RealPathUtil.getRealPathFromURI_API11to18(context, fileUri);
}
// SDK > 19 (Android 4.4) and up
else {
realPath = RealPathUtil.getRealPathFromURI_API19(context, fileUri);
}
return realPath;
}
@SuppressLint("NewApi")
public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
String[] proj = {MediaStore.Images.Media.DATA};
String result = null;
CursorLoader cursorLoader = new CursorLoader(context, contentUri, proj, null, null, null);
Cursor cursor = cursorLoader.loadInBackground();
if (cursor != null) {
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);
cursor.close();
}
return result;
}
public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri) {
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
int column_index = 0;
String result = "";
if (cursor != null) {
column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
result = cursor.getString(column_index);
cursor.close();
return result;
}
return result;
}
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
* other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @author paulburke
*/
@SuppressLint("NewApi")
public static String getRealPathFromURI_API19(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[]{
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
}
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
这是精确解。只需打电话 Uri selectedResume = data.getData(); String mediaResumePath = RealPathUtil.getRealPath(this, selectedResume);
这就是我下载、保存并return一个文件到视图模型的方式:)
接口文件下载器{ 伴生对象{ 私有 val 实例:FileDownloader by lazy {
val loggingInterceptor = HttpLoggingInterceptor()
loggingInterceptor.level = HttpLoggingInterceptor.Level.HEADERS
val client = OkHttpClient.Builder()
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.callTimeout(300, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
.build()
val downloaderApi = Retrofit.Builder()
.baseUrl("https://exmaple.com")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(FileDownloader::class.java)
downloaderApi
}
suspend fun downloadAndSaveFile(
url: String
): File {
val file = getPdfFile()
val responseBody = try {
instance.downloadFile(url)
} catch (e: Exception) {
throw FileDownloadException(url, e)
}
@Suppress("BlockingMethodInNonBlockingContext")
try {
FileOutputStream(file).use { outputStream ->
responseBody.byteStream().use { inputStream ->
inputStream.copyTo(outputStream)
}
}
} catch (e: Exception) {
throw FileDownloadException(url, e)
}
return file
}
}
@GET
@Streaming
suspend fun downloadFile(@Url utl: String): ResponseBody
}
fun getPdfFile(): File {
File(App.appContext.filesDir, "pdfs").apply { mkdirs() }
return File(App.appContext.filesDir, "tempPDF.pdf")
}
class FileDownloadException(url: String, e: Exception) : Throwable()