在 onActivityResult 中收到 "content://" Uri 后从 MediaStore 获取 PDF?
Get PDF from MediaStore after receiving "content://" Uri in onActivityResult?
我开始 ACTION_GET_CONTENT
意图以选择 PDF:
override fun routeToFilePicker() {
val intent = Intent()
intent.type = MediaType.PDF.toString()
intent.action = Intent.ACTION_GET_CONTENT
activity.startActivityForResult(
Intent.createChooser(intent, "Select PDF"),
REQUEST_CODE_PDF_PICKER
)
}
然后 onActivityResult
我尝试从 Uri (content//:path
) 创建一个 PDF:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_PDF_PICKER ) {
data?.data?.let { pdfUri: Uri ->
val pdfFile: File = pdfUri.toFile() <-- chrash
...
}
}
}
pdfUri.toFile()
导致致命异常:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1003, result=-1, data=Intent { dat=content://com.android.providers.downloads.documents/document/3569 flg=0x1 }} to activity {my.package.name.activity}: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569
Caused by: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569
我需要一个文件才能将页面转换为图像。
如何从 MediaStore 返回的 Uri 中获取 PDF 作为文件?
public static File getFileFromUri(Uri uri, Context context) {
if (uri == null) {
return null;
}
switch (uri.getScheme()) {
case "content":
return getFileFromContentUri(uri, context);
case "file":
return new File(uri.getPath());
default:
return null;
}
}
private static File getFileFromContentUri(Uri contentUri, Context context) {
if (contentUri == null) {
return null;
}
File file = null;
String filePath;
String fileName;
String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(contentUri, filePathColumn, null,
null, null);
if (cursor != null) {
cursor.moveToFirst();
filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
fileName = cursor.getString(cursor.getColumnIndex(filePathColumn[1]));
cursor.close();
if (!TextUtils.isEmpty(filePath)) {
file = new File(filePath);
}
if (!file.exists() || file.length() <= 0 || TextUtils.isEmpty(filePath)) {
filePath = getPathFromInputStreamUri(context, contentUri, fileName);
}
if (!TextUtils.isEmpty(filePath)) {
file = new File(filePath);
}
}
return file;
}
这就是我获取 pdf 文件的方式:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "application/pdf"
addCategory(Intent.CATEGORY_OPENABLE)
flags = flags or Intent.FLAG_GRANT_READ_URI_PERMISSION
}
startActivityForResult(intent, 111)
在你的OnActivityResult(requestCode:Int,resultCode:Int,data:Intent?)
里面
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
// 101 -> {
// data?.data?.also { uri ->
// Log.i(TAG, "Uri: $uri")
// baseAdapter?.add(ImageArray(null, null, uri.toString()))
// }
// }
111 -> {
data?.data?.also { documentUri ->
baseActivity.contentResolver?.takePersistableUriPermission(
documentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val file = DocumentUtils.getFile(baseActivity,documentUri)//use pdf as file
}
}
}
}
单例 class 将 Uri 隐藏到文件:
object DocumentUtils {
fun getFile(mContext: BaseActivity?, documentUri: Uri): File {
val inputStream = mContext?.contentResolver?.openInputStream(documentUri)
var file = File("")
inputStream.use { input ->
file =
File(mContext?.cacheDir, System.currentTimeMillis().toString()+".pdf")
FileOutputStream(file).use { output ->
val buffer =
ByteArray(4 * 1024) // or other buffer size
var read: Int = -1
while (input?.read(buffer).also {
if (it != null) {
read = it
}
} != -1) {
output.write(buffer, 0, read)
}
output.flush()
}
}
return file
}
}
P.S: 不要忘记在运行时请求权限
Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.READ_EXTERNAL_STORAGE
随着最新的 API 更新,Android 已推动使用内容解析器进行文件相关处理。
使用接收到的路径,同样需要使用内容解析器来解析。。
样品可在 Google Github
总的来说,选择文件后收到的 URI 需要像下面这样在 Fragment 中传递。
创建用于显示 PDF 的片段。并且会有关联的ViewModel。
val parcelFileDescriptor = activity?.contentResolver?.openFileDescriptor(fileURI, "r")
在ViewModel中,借助Coroutine的帮助,我们将使用如下流程
val scope = CoroutineScope(executor.asCoroutineDispatcher() + job)
scope.launch {
openPdfRenderer()
showPage(0)
}
fun openPdfRenderer() {
pdfRenderer = PdfRenderer(fileURI!!) //android.graphics.pdf.PdfRenderer
}
fun showPage(index: Int) {
currentPage?.let { page ->
currentPage = null
page.close()
}
pdfRenderer?.let { renderer ->
val page = renderer.openPage(index).also {
currentPage = it
}
val bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888)
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT)
_pageBitmap.postValue(bitmap)
val count = renderer.pageCount
_pageInfo.postValue(index to count)
_previousEnabled.postValue(index > 0)
_nextEnabled.postValue(index + 1 < count)
}
}
上找到完整的示例源
我同意,这似乎是一项开销!但由于多种原因,Android 地理环境也发生了变化。
祝您编程愉快!干杯!
我开始 ACTION_GET_CONTENT
意图以选择 PDF:
override fun routeToFilePicker() {
val intent = Intent()
intent.type = MediaType.PDF.toString()
intent.action = Intent.ACTION_GET_CONTENT
activity.startActivityForResult(
Intent.createChooser(intent, "Select PDF"),
REQUEST_CODE_PDF_PICKER
)
}
然后 onActivityResult
我尝试从 Uri (content//:path
) 创建一个 PDF:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_PDF_PICKER ) {
data?.data?.let { pdfUri: Uri ->
val pdfFile: File = pdfUri.toFile() <-- chrash
...
}
}
}
pdfUri.toFile()
导致致命异常:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1003, result=-1, data=Intent { dat=content://com.android.providers.downloads.documents/document/3569 flg=0x1 }} to activity {my.package.name.activity}: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569
Caused by: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569
我需要一个文件才能将页面转换为图像。
如何从 MediaStore 返回的 Uri 中获取 PDF 作为文件?
public static File getFileFromUri(Uri uri, Context context) {
if (uri == null) {
return null;
}
switch (uri.getScheme()) {
case "content":
return getFileFromContentUri(uri, context);
case "file":
return new File(uri.getPath());
default:
return null;
}
}
private static File getFileFromContentUri(Uri contentUri, Context context) {
if (contentUri == null) {
return null;
}
File file = null;
String filePath;
String fileName;
String[] filePathColumn = {MediaStore.MediaColumns.DATA, MediaStore.MediaColumns.DISPLAY_NAME};
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(contentUri, filePathColumn, null,
null, null);
if (cursor != null) {
cursor.moveToFirst();
filePath = cursor.getString(cursor.getColumnIndex(filePathColumn[0]));
fileName = cursor.getString(cursor.getColumnIndex(filePathColumn[1]));
cursor.close();
if (!TextUtils.isEmpty(filePath)) {
file = new File(filePath);
}
if (!file.exists() || file.length() <= 0 || TextUtils.isEmpty(filePath)) {
filePath = getPathFromInputStreamUri(context, contentUri, fileName);
}
if (!TextUtils.isEmpty(filePath)) {
file = new File(filePath);
}
}
return file;
}
这就是我获取 pdf 文件的方式:
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
type = "application/pdf"
addCategory(Intent.CATEGORY_OPENABLE)
flags = flags or Intent.FLAG_GRANT_READ_URI_PERMISSION
}
startActivityForResult(intent, 111)
在你的OnActivityResult(requestCode:Int,resultCode:Int,data:Intent?)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
// 101 -> {
// data?.data?.also { uri ->
// Log.i(TAG, "Uri: $uri")
// baseAdapter?.add(ImageArray(null, null, uri.toString()))
// }
// }
111 -> {
data?.data?.also { documentUri ->
baseActivity.contentResolver?.takePersistableUriPermission(
documentUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION
)
val file = DocumentUtils.getFile(baseActivity,documentUri)//use pdf as file
}
}
}
}
单例 class 将 Uri 隐藏到文件:
object DocumentUtils {
fun getFile(mContext: BaseActivity?, documentUri: Uri): File {
val inputStream = mContext?.contentResolver?.openInputStream(documentUri)
var file = File("")
inputStream.use { input ->
file =
File(mContext?.cacheDir, System.currentTimeMillis().toString()+".pdf")
FileOutputStream(file).use { output ->
val buffer =
ByteArray(4 * 1024) // or other buffer size
var read: Int = -1
while (input?.read(buffer).also {
if (it != null) {
read = it
}
} != -1) {
output.write(buffer, 0, read)
}
output.flush()
}
}
return file
}
}
P.S: 不要忘记在运行时请求权限
Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.READ_EXTERNAL_STORAGE
随着最新的 API 更新,Android 已推动使用内容解析器进行文件相关处理。
使用接收到的路径,同样需要使用内容解析器来解析。。
样品可在 Google Github
总的来说,选择文件后收到的 URI 需要像下面这样在 Fragment 中传递。
创建用于显示 PDF 的片段。并且会有关联的ViewModel。
val parcelFileDescriptor = activity?.contentResolver?.openFileDescriptor(fileURI, "r")
在ViewModel中,借助Coroutine的帮助,我们将使用如下流程
val scope = CoroutineScope(executor.asCoroutineDispatcher() + job)
scope.launch {
openPdfRenderer()
showPage(0)
}
fun openPdfRenderer() {
pdfRenderer = PdfRenderer(fileURI!!) //android.graphics.pdf.PdfRenderer
}
fun showPage(index: Int) {
currentPage?.let { page ->
currentPage = null
page.close()
}
pdfRenderer?.let { renderer ->
val page = renderer.openPage(index).also {
currentPage = it
}
val bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888)
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT)
_pageBitmap.postValue(bitmap)
val count = renderer.pageCount
_pageInfo.postValue(index to count)
_previousEnabled.postValue(index > 0)
_nextEnabled.postValue(index + 1 < count)
}
}
上找到完整的示例源
我同意,这似乎是一项开销!但由于多种原因,Android 地理环境也发生了变化。
祝您编程愉快!干杯!