如何从 URL 打开使用身份验证的 PDF?
How to open a PDF from URL that uses authentication?
我必须从需要访问令牌的 URL 打开 PDF(该 PDF 未公开提供)。 PDF 不应永久存储在设备上。
如果不需要访问令牌,解决方案如下所示:
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(Uri.parse(url), "application/pdf") }
startActivity(intent)
但是,需要访问令牌,因此这不起作用。
我试过在应用程序中下载文件,然后打开文件的本地副本,如下所示:
val inputStream = /*get PDF as an InputStream*/
val file = File.createTempFile(
fileName,
".pdf",
context?.externalCacheDir
).apply { deleteOnExit() }
stream.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(Uri.fromFile(file), "application/pdf") }
startActivity(intent)
但这给了我 android.os.FileUriExposedException
消息 <myFilePath> exposed beyond app through Intent.getData()
。
我也使用 context?.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
尝试了同样的事情并得到了相同的结果。
到目前为止,我得到的最接近的是这样的:
val inputStream = /*get PDF as an InputStream*/
val file = File.createTempFile(
fileName,
".pdf",
context?.filesDir
).apply { deleteOnExit() }
stream.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(Uri.parse(file.absolutePath), "application/pdf") }
startActivity(intent)
这样做,我的三星设备向我提供了三星笔记和 Microsoft Word 来打开 PDF,但实际上它们都无法打开。 (它刚刚打开这些应用程序,它们显示错误,找不到它们应该打开的文件)
已安装的 PDF reader - 使用我在最顶部的两行代码时通常会打开 PDF 的那个 post - 甚至没有列出。
那么,如何正确显示此 PDF?
编辑:
找到后,我添加了一个文件提供程序,现在我的代码如下所示:
val inputStream = /*get PDF as an InputStream*/
val file = File.createTempFile(
fileName,
".pdf",
context?.getExternalFilesDir(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
Environment.DIRECTORY_DOCUMENTS
else
Environment.DIRECTORY_DOWNLOADS
).apply { deleteOnExit() }
stream.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
val documentUri: Uri = FileProvider.getUriForFile(
requireContext(),
getString(R.string.file_provider_authority),
file
)
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(documentUri, "application/pdf") }
startActivity(intent)
现在PDF可以正常打开了。但是 PDF 是空的。
这是我将 PDF 加载到 InputStream 的方式:
interface DocumentsClient {
@GET("/api/something/pdf/{pdf_id}")
@Headers("Accept: application/pdf")
@Streaming
fun loadSomethingPDF(@Path("pdf_id") pdfId: Long): Call<ResponseBody>
}
fun loadSomethingPDF(accessToken: String?, pdfId: Long) =
createDocumentsClient(accessToken).loadSomethingPDF(pdfId)
private fun createDocumentsClient(accessToken: String?): DocumentsClient {
val okClientBuilder = OkHttpClient.Builder()
// add headers
okClientBuilder.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
if (accessToken != null) {
requestBuilder.header("Authorization", "Bearer $accessToken")
}
val request = requestBuilder.build()
chain.proceed(request)
}
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(okClientBuilder.build())
.build()
return retrofit.create(DocumentsClient::class.java)
}
loadSomethingPdf 然后被这样调用:
@Throws(IOException::class, IllegalStateException::class)
suspend fun loadSomethingPdf(accessToken: String?, pdfId: Long) = withContext(Dispatchers.IO) {
val call = remoteManager.loadSomethingPDF(accessToken, pdfId)
try {
val response = call.execute()
if (!response.isSuccessful) {
return@withContext null
}
return@withContext response.body()?.byteStream()
} catch (e: IOException) {
throw e
} catch (e: IllegalStateException) {
throw e
}
}
这就是我从中获取 inputStream 的地方。
好的,我的问题完全不同。
我需要授予 Uri 权限以允许其他应用程序读取我发送给它们的 Uri。
代码只需要这个:
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
我必须从需要访问令牌的 URL 打开 PDF(该 PDF 未公开提供)。 PDF 不应永久存储在设备上。
如果不需要访问令牌,解决方案如下所示:
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(Uri.parse(url), "application/pdf") }
startActivity(intent)
但是,需要访问令牌,因此这不起作用。
我试过在应用程序中下载文件,然后打开文件的本地副本,如下所示:
val inputStream = /*get PDF as an InputStream*/
val file = File.createTempFile(
fileName,
".pdf",
context?.externalCacheDir
).apply { deleteOnExit() }
stream.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(Uri.fromFile(file), "application/pdf") }
startActivity(intent)
但这给了我 android.os.FileUriExposedException
消息 <myFilePath> exposed beyond app through Intent.getData()
。
我也使用 context?.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
尝试了同样的事情并得到了相同的结果。
到目前为止,我得到的最接近的是这样的:
val inputStream = /*get PDF as an InputStream*/
val file = File.createTempFile(
fileName,
".pdf",
context?.filesDir
).apply { deleteOnExit() }
stream.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(Uri.parse(file.absolutePath), "application/pdf") }
startActivity(intent)
这样做,我的三星设备向我提供了三星笔记和 Microsoft Word 来打开 PDF,但实际上它们都无法打开。 (它刚刚打开这些应用程序,它们显示错误,找不到它们应该打开的文件)
已安装的 PDF reader - 使用我在最顶部的两行代码时通常会打开 PDF 的那个 post - 甚至没有列出。
那么,如何正确显示此 PDF?
编辑:
找到
val inputStream = /*get PDF as an InputStream*/
val file = File.createTempFile(
fileName,
".pdf",
context?.getExternalFilesDir(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
Environment.DIRECTORY_DOCUMENTS
else
Environment.DIRECTORY_DOWNLOADS
).apply { deleteOnExit() }
stream.use { input ->
file.outputStream().use { output ->
input.copyTo(output)
}
}
val documentUri: Uri = FileProvider.getUriForFile(
requireContext(),
getString(R.string.file_provider_authority),
file
)
val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(documentUri, "application/pdf") }
startActivity(intent)
现在PDF可以正常打开了。但是 PDF 是空的。
这是我将 PDF 加载到 InputStream 的方式:
interface DocumentsClient {
@GET("/api/something/pdf/{pdf_id}")
@Headers("Accept: application/pdf")
@Streaming
fun loadSomethingPDF(@Path("pdf_id") pdfId: Long): Call<ResponseBody>
}
fun loadSomethingPDF(accessToken: String?, pdfId: Long) =
createDocumentsClient(accessToken).loadSomethingPDF(pdfId)
private fun createDocumentsClient(accessToken: String?): DocumentsClient {
val okClientBuilder = OkHttpClient.Builder()
// add headers
okClientBuilder.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
if (accessToken != null) {
requestBuilder.header("Authorization", "Bearer $accessToken")
}
val request = requestBuilder.build()
chain.proceed(request)
}
val retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(okClientBuilder.build())
.build()
return retrofit.create(DocumentsClient::class.java)
}
loadSomethingPdf 然后被这样调用:
@Throws(IOException::class, IllegalStateException::class)
suspend fun loadSomethingPdf(accessToken: String?, pdfId: Long) = withContext(Dispatchers.IO) {
val call = remoteManager.loadSomethingPDF(accessToken, pdfId)
try {
val response = call.execute()
if (!response.isSuccessful) {
return@withContext null
}
return@withContext response.body()?.byteStream()
} catch (e: IOException) {
throw e
} catch (e: IllegalStateException) {
throw e
}
}
这就是我从中获取 inputStream 的地方。
好的,我的问题完全不同。
我需要授予 Uri 权限以允许其他应用程序读取我发送给它们的 Uri。
代码只需要这个:
intent.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION