问题描述
我正在从服务器下载 PDF 文件并将响应正文字节流传递到下面的函数中,该函数成功地将 PDF 文件存储在用户下载文件夹中。
@RequiresApi(Build.VERSION_CODES.Q)
fun saveDownload(pdfInputStream: InputStream) {
val values = ContentValues().apply {
put(MediaStore.Downloads.disPLAY_NAME,"test")
put(MediaStore.Downloads.MIME_TYPE,"application/pdf")
put(MediaStore.Downloads.IS_PENDING,1)
}
val resolver = context.contentResolver
val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val itemUri = resolver.insert(collection,values)
if (itemUri != null) {
resolver.openFileDescriptor(itemUri,"w").use { parcelFileDescriptor ->
ParcelFileDescriptor.AutoCloSEOutputStream(parcelFileDescriptor)
.write(pdfInputStream.readBytes())
}
values.clear()
values.put(MediaStore.Downloads.IS_PENDING,0)
resolver.update(itemUri,values,null,null)
}
}
现在一旦这个函数返回,我想打开保存的 PDF 文件。我已经尝试了几种方法来让它工作,但选择器总是说没有任何东西可以打开文件。我认为仍然存在权限问题(也许我使用的 FileProvider 错误?),或者路径错误,或者完全是其他问题。
以下是我尝试过的几个示例:
fun uriFromFile(context: Context,file: File): Uri {
return FileProvider.getUriForFile(context,BuildConfig.APPLICATION_ID + ".provider",file)
}
a)
val openIntent = Intent(Intent.ACTION_VIEW)
openIntent.putExtra(Intent.EXTRA_STREAM,uriFromFile(this,File(this.getExternalFilesDir(DIRECTORY_DOWNLOADS)?.absolutePath.toString(),"test")))
openIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
openIntent.type = "application/pdf"
startActivity(Intent.createChooser(openIntent,"share.."))
b)
val shareIntent = Intent(Intent.ACTION_SEND)
shareIntent.putExtra(Intent.EXTRA_STREAM,File(this.getExternalFilesDir(null)?.absolutePath.toString(),"test.pdf")))
shareIntent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
shareIntent.type = "application/pdf"
startActivity(Intent.createChooser(shareIntent,"share.."))
c)
val file = File(itemUri.toString()) //itemUri from the saveDownload function
val target = Intent(Intent.ACTION_VIEW)
val newFile = FileProvider.getUriForFile(this,file);
target.setDataAndType(newFile,"application/pdf")
target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
val intent = Intent.createChooser(target,"Open File")
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
ContextCompat.startActivity(this,intent,null)
d)
val target = Intent(Intent.ACTION_VIEW)
target.setDataAndType(Uri.parse("content://media/external_primary/downloads/2802"),"application/pdf"
target.flags = Intent.FLAG_ACTIVITY_NO_HISTORY
val intent = Intent.createChooser(target,null)
(也在这个 URI 的末尾尝试 /test.pdf,并用我的权限名称替换媒体)
<provider
android:name="androidx.core.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>
@xml/provider_paths 如下,尽管除此之外我还尝试了各种组合,包括路径为“.”:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="files_root" path="/"/>
<files-path name="files_root" path="/"/>
<external-path name="files_root" path="/"/>
</paths>
作为旁注,肯定有能够打开 PDF 的选择器,进入文件资源管理器并从那里打开它可以正常工作。尝试共享而不是打开共享时也会失败。
解决方法
按照此步骤和代码,它将管理从下载您的 pdf 到打开它的所有内容。
创建一个类名DownloadTask并把完整的代码放在下面
public class DownloadTask {
private static final String TAG = "Download Task";
private Context context;
private String downloadFileUrl = "",downloadFileName = "";
private ProgressDialog progressDialog;
long downloadID;
private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
@Override
public void onReceive(Context context,Intent intent) {
//Fetching the download id received with the broadcast
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID,-1);
//Checking if the received broadcast is for our enqueued download by matching download id
if (downloadID == id) {
downloadCompleted(downloadID);
}
}
};
public DownloadTask(Context context,String downloadUrl) {
this.context = context;
this.downloadFileUrl = downloadUrl;
downloadFileName = downloadFileUrl.substring(downloadFileUrl.lastIndexOf('/') + 1);//Create file name by picking download file name from URL
Log.e(TAG,downloadFileName);
context.registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
downloadFile(downloadFileUrl);
}
public void downloadFile(String url) {
try {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(),downloadFileName);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url))
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)// Visibility of the download Notification
.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS,downloadFileName
)
.setDestinationUri(Uri.fromFile(file))
.setTitle(downloadFileName)// Title of the Download Notification
.setDescription("Downloading")// Description of the Download Notification
.setAllowedOverMetered(true)// Set if download is allowed on Mobile network
.setAllowedOverRoaming(true);// Set if download is allowed on roaming network
request.allowScanningByMediaScanner();
DownloadManager downloadManager = (DownloadManager) context.getSystemService(DOWNLOAD_SERVICE);
downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Downloading...");
progressDialog.setCancelable(false);
progressDialog.show();
} catch (Exception e) {
Log.d("Download",e.toString());
}
}
void downloadCompleted(long downloadID) {
progressDialog.dismiss();
new AlertDialog.Builder(context)
.setTitle("Document")
.setMessage("Document Downloaded Successfully")
.setPositiveButton("Open",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,int which) {
openDownloadedAttachment(downloadID);
}
})
// A null listener allows the button to dismiss the dialog and take no further action.
.setNegativeButton(android.R.string.no,null)
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
context.unregisterReceiver(onDownloadComplete);
}
Uri path;
private void openDownloadedAttachment(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) {
path = FileProvider.getUriForFile(context,context.getApplicationContext().getPackageName() + ".provider",new File(Uri.parse(downloadLocalUri).getPath()));
//path = Uri.parse(downloadLocalUri);
Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
pdfIntent.setDataAndType(path,downloadMimeType);
pdfIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_TOP);
try {
context.startActivity(pdfIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(context,"No Application available to view PDF",Toast.LENGTH_SHORT).show();
}
}
}
cursor.close();
}
}
然后从您的活动中像这样下载您的 pdf。
new DownloadTask(this,"PDF_URL");
来自你的片段
new DownloadTask(getContext(),"PDF_URL");
下载完成后,它会自动打开您的 pdf。