上下文:安卓 10,API 29 .
我打印了一个由 WebView
生成的 PDF 文件,但现在我想将其保存到文件中。所以我尝试了Intent.ACTION_CREATE_DOCUMENT
选择文件并通过 printAdapter
保存的onWrite
方法。
问题是 文件始终为空 - 0 个字节 - 并且不会引发错误。它只是调用onWriteFailed
,但有一个空的错误消息。choosenFileUri
具有类似 content://com.android.providers.downloads.documents/document/37
的值
我用来开始选择新文件的 Intent 的方法。请注意,此 Activity 的结果是 Uri
:
fun startIntentToCreatePdfFile(fragment: Fragment, filename : String) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
putExtra(Intent.EXTRA_TITLE, filename)
}
fragment.startActivityForResult(intent, IntentCreatePdfDocument)
}
我用来将PDF“打印”到文件的方法。 fileUri
来自Intent.ACTION_CREATE_DOCUMENT
:fun printPdfToFile(
context: Context,
webView: WebView,
fileUri: Uri
) {
(context.getSystemService(Context.PRINT_SERVICE) as? PrintManager)?.let {
val jobName = "Print PDF to save it"
val printAdapter = webView.createPrintDocumentAdapter(jobName)
val printAttributes = PrintAttributes.Builder()
.setMediaSize(PrintAttributes.MediaSize.ISO_A4)
.setResolution(PrintAttributes.Resolution("pdf", "pdf", 600, 600))
.setMinMargins(PrintAttributes.Margins.NO_MARGINS).build()
printAdapter.onLayout(null, printAttributes, null, object : LayoutResultCallback() {
override fun onLayoutFinished(info: PrintDocumentInfo, changed: Boolean) {
context.contentResolver.openFileDescriptor(fileUri, "w")?.use {
printAdapter.onWrite(
arrayOf(PageRange.ALL_PAGES),
it,
CancellationSignal(),
object : WriteResultCallback() {
})
}
}
}, null)
}
}
我做什么选择文件onActivityResult
:override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != Activity.RESULT_OK) {
return null
}
if (requestCode != IntentCreatePdfDocument) {
throw Exception("RequestCode not implemented: $requestCode")
}
val choosenFileUri = data?.data
// If it is null, nothing to do
if (choosenFileUri == null) {
return
}
try {
HtmlHelpers.savePdfFromHtml(
requireContext(),
"html document to be represented in the WebView",
choosenFileUri)
} catch (exception: Exception) {
_logger.error(exception)
Helpers.showError(requireActivity(), getString(R.string.generic_error))
}
dismiss()
}
...在哪里 HtmlHelpers.savePdfFromHtml
是:fun savePdfFromHtml(
context: Context,
htmlContent: String,
fileUri: Uri
) {
generatePdfFromHtml(
context,
htmlContent
) { webView ->
PrintHelpers.printPdfToFile(
context,
webView,
fileUri)
}
}
...和 generatePdfFromHtml
是:private fun generatePdfFromHtml(
context: Context,
htmlContent: String,
onPdfCreated: (webView: WebView) -> Unit
) {
val webView = WebView(context)
webView.settings.javaScriptEnabled = true
webView.webViewClient = object : WebViewClient() {
override fun onPageFinished(webView: WebView, url: String) {
onPdfCreated(webView)
}
}
webView.loadDataWithBaseURL(
null,
htmlContent,
"text/html; charset=utf-8",
"UTF-8",
null);
}
我检查了有关此主题的所有其他答案,但每个人都手动创建
ParcelFileDescriptor
而不是 it
在 onWrite
方法。每个人都会做这样的事情:fun getOutputFile(path: File, fileName: String): ParcelFileDescriptor? {
val file = File(path, fileName)
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)
}
但我不能这样做,因为我只有 Uri。编辑:正如@blackapps 所建议的,我在得到
FileDescriptor
后尝试打开输出流, 但是 我仍然得到相同的结果 :context.contentResolver.openFileDescriptor(fileUri, "w")?.use {
val fileDescriptor = it
FileOutputStream(it.fileDescriptor).use {
printAdapter.onWrite(
arrayOf(PageRange.ALL_PAGES),
fileDescriptor,
CancellationSignal(),
object : WriteResultCallback() {
})
}
}
最佳答案
为了从 HTML 内容生成 PDF 文件,我使用了 Library
我将 pdf 文件存储在 download
中共享存储文件夹(外部)。使用以下方法检索位置。
//fileName is the name of the file that you want to save.
fun getSavedFile(context: Context, fileName: String): File {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)!!,
"$fileName.pdf"
)
}
return File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"$fileName.pdf"
)
}
然后使用库内置方法从 HTML
生成 PDF WebView
内的内容加载//webView is the ID of WebView where we are loading the html content
// fileName : Pass whatever name you want.
PDFUtil.generatePDFFromWebView(pdfDownloadUtil.getSavedFile(getApplicationContext(), fileName), webView, new PDFPrint.OnPDFPrintListener() {
@Override
public void onSuccess(File file) {
savedPdfFile = file;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createFile(Uri.fromFile(file));
}
}
@Override
public void onError(Exception exception) {
}
});
}
现在,我们需要在获取传递的 html 内容的 pdf 后触发 Intent 。 private void createFile(Uri pickerInitialUri) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, fileName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
}
createFileResult.launch(intent);
}
ActivityResultLauncher<Intent> createFileResult = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == AppCompatActivity.RESULT_OK) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
pdfDownloadUtil.writeFileContent(this, savedPdfFile, result.getData().getData(), () -> {
showSnackToOpenPdf();
return null;
});
}
}
}
);
在 oreo 或以上设备中,我们使用上述方法,即 写入文件内容 @RequiresApi(Build.VERSION_CODES.O)
fun writeFileContent(
context: @NotNull Context,
savedPdfFile: @NotNull File,
uri: @NotNull Uri,
listener: () -> Unit
) {
try {
val file = uri.let { context.contentResolver.openFileDescriptor(it, "w") }
file?.let {
val fileOutputStream = FileOutputStream(it.fileDescriptor)
fileOutputStream.write(Files.readAllBytes(savedPdfFile.toPath()))
fileOutputStream.close()
it.close()
}
listener()
} catch (e: FileNotFoundException) {
//print logs
e.printStackTrace()
} catch (e: IOException) {
//print logs
e.printStackTrace()
}
}
注:如果页数很大,即超过 200 页。然后它将无法在内部工作,因为它会缓存 WebView
中的页面然后加载它。因此,另一种方法是从 API 获取 PDF 文件的链接。
关于android - 使用 Intent.ACTION_CREATE_DOCUMENT 选择文件后保存从 WebView 生成的 PDF 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63922751/