下载在 WKWebView 中加载的嵌入式 PDF
Download embedded PDF loaded in WKWebView
从 url 加载 HTML5
页面时,我在该页面某处获取 pdf,我必须下载该 pdf 或将其另存为 base64
。
这是 pdf 在 HTML 代码中的位置。我不能简单地点击 'src' URL 并获取 pdf。
< embed width="100%" height="100%" name="plugin" id="plugin" src="https://myurl.com/fileToOpen.pdf” type="application/pdf" internalinstanceid="8" title="">
任何 JS 可以帮助我获取 base64
字符串或任何其他下载方法?
PS: 使用答案作为评论,因为我需要格式化
您应该在 webview
中执行以下 JavaScript
path = document.getElementById("plugin").src
fetch(path).then(function (response) {
response.body.getReader().read().then(function(result) {
return btoa(String.fromCharCode.apply(null, result.value));
}).then(function(b64) {
window.pdf_data = b64;
});
});
然后您可以执行另一个查询来访问 window.pdf_data
假设从 javascript 执行中获取 return 值是可能的?
您是要将 PDF 下载到您的 iPhone 还是 Mac?通常,无法将 PDF 直接下载到您的 iPhone,因为 iPhone 本身不具备存储 PDF 的功能。您需要有 iBooks 或 iCloud Drive,然后在另一个 window 中打开 PDF,然后手动下载它。下载 PDF 之前仍然需要用户交互,这意味着用户必须批准下载。通过将 JavaScript 注入到 WKWebView
实例中直接下载是不可能的。
更新
从Docs他们说
The Fetch API provides an interface for fetching resources (including
across the network). It will seem familiar to anyone who has used
XMLHttpRequest
您还可以使用以下字符串从 WKWebview 中获取 base64 字符串
let s = "path = document.getElementById(\"plugin\").src\n" +
"\n" +
"fetch(path).then(function (response) {\n" +
" response.body.getReader().read().then(function(result) {\n" +
" return btoa(String.fromCharCode.apply(null, result.value));\n" +
" }).then(function(b64) {\n" +
" window.webkit.messageHandlers.myInterface.postMessage(b64);\n" +
" });\n" +
"});"
fetch 和 xmlhttp 都是异步工作的。您需要做的就是等待处理完成后使用 javascript 到 ios 的桥将其传递给 Swift( WKScriptMessageHandler)
使用以下代码获取从javascript到Swift的base64字符串。
我正在使用 WKScriptMessageHandler 从 Javascript 获取回调,当 base64 字符串准备好被使用时 。在 String 中,你只需要传递 pdf 的 url 它会做一个 ajax 请求来获取 pdf 文件,然后将其转换为 base64 字符串。
import UIKit
import WebKit
class ViewController: UIViewController {
@IBOutlet weak var btnPDF: UIButton!
@IBOutlet weak var webViewParentView: UIView!
var activityIndicator: UIActivityIndicatorView?
var webView: WKWebView!
@objc func didSelect(_ sender: UIView){
let s="var xhr = new XMLHttpRequest();\n" +
"xhr.open(\'GET\', \"https://codingexceptions.com/wkwebview/dummy.pdf\", true);\n" +
"\n" +
"xhr.responseType = \'arraybuffer\';\n" +
"\n" +
"xhr.onload = function(e) {\n" +
" if (this.status == 200) {\n" +
" var uInt8Array = new Uint8Array(this.response);\n" +
" var i = uInt8Array.length;\n" +
" var binaryString = new Array(i);\n" +
" while (i--)\n" +
" {\n" +
" binaryString[i] = String.fromCharCode(uInt8Array[i]);\n" +
" }\n" +
" var data = binaryString.join(\'\');\n" +
"\n" +
" var base64 = window.btoa(data);\n" +
"\n" +
"window.webkit.messageHandlers.myInterface.postMessage(base64);" +
"\n" +
" }\n" +
"};\n" +
"\n" +
"xhr.send();\n"
webView.configuration.userContentController.add(self, name: "myInterface")
webView?.evaluateJavaScript(s, completionHandler: {(string,error) in
print(error ?? "no error")
})
}
func setupWebView(){
webView = WKWebView.init(frame: CGRect(x: 0, y: 0, width: webViewParentView.frame.width, height: webViewParentView.frame.height))
webView.navigationDelegate = self
webViewParentView.addSubview(webView)
activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicator?.center = self.view.center
self.view.addSubview(activityIndicator!)
webView.load(URLRequest(url: URL(string: "https://codingexceptions.com/wkwebview/index.php")!))
activityIndicator?.startAnimating()
}
override func viewDidLoad() {
super.viewDidLoad()
btnPDF.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupWebView()
}
}
extension ViewController: WKScriptMessageHandler{
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("Message received: \(message.name) with body: \(message.body)")
}
}
extension ViewController: WKNavigationDelegate{
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.activityIndicator?.stopAnimating()
self.activityIndicator?.removeFromSuperview()
self.activityIndicator = nil
}
}
更新:按照@Tarun 的回答
从嵌入标签中获取源代码
只需将下面的 行放在字符串变量 s 的开头,并在 xhr.open
中传递 url
var url = document.getElementById("plugin").src
这个问题有时会反问,
但是,如果有人正在寻找 swift 解决方案 WKWebView 以下载 .pdf 或文件管理器上的任何文件,这就是我结束的方式-向上
class WebPortalVC: UIViewController, WKNavigationDelegate,WKUIDelegate, UIDocumentInteractionControllerDelegate,URLSessionDownloadDelegate {
覆盖以下函数,它将拦截 url,在我们的例子中,我们检查以 .pdf 和 .csv 结尾的 ulr 并重定向以使用文件管理器视图打开。启用查看文件、下载并保存到设备存储、空投或与其他应用程序共享
只需添加以下功能并检查即可。
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
print("fileDownload: check :: \(url)")
let extention = "\(url)".suffix(4)
if extention == ".pdf" || extention == ".csv"{
print("fileDownload: redirect to download events. \(extention)")
DispatchQueue.main.async {
self.downloadPDF(tempUrl: "\(url)")
}
decisionHandler(.cancel)
return
}
}
decisionHandler(.allow)
}
func downloadPDF(tempUrl:String){
print("fileDownload: downloadPDF")
guard let url = URL(string: tempUrl) else { return }
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
let downloadTask = urlSession.downloadTask(with: url)
downloadTask.resume()
//showHUD(isShowBackground: true); //show progress if you need
}
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
print("fileDownload: documentInteractionControllerViewControllerForPreview")
return self
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// create destination URL with the original pdf name
print("fileDownload: urlSession")
guard let url = downloadTask.originalRequest?.url else { return }
print("fileDownload: urlSession \(url)")
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destinationURL = documentsPath.appendingPathComponent(url.lastPathComponent)
// delete original copy
try? FileManager.default.removeItem(at: destinationURL)
// copy from temp to Document
do {
try FileManager.default.copyItem(at: location, to: destinationURL)
myViewDocumentsmethod(PdfUrl:destinationURL)
print("fileDownload: downloadLocation", destinationURL)
DispatchQueue.main.async {
NBMaterialToast.showWithText(self.view, text: "Download Completed", duration: NBLunchDuration.long)
}
} catch let error {
print("fileDownload: error \(error.localizedDescription)")
}
// dismissHUD(isAnimated: false); //dismiss progress
}
func myViewDocumentsmethod(PdfUrl:URL){
print("fileDownload: myViewDocumentsmethod \(PdfUrl)")
DispatchQueue.main.async {
let controladorDoc = UIDocumentInteractionController(url: PdfUrl)
controladorDoc.delegate = self
controladorDoc.presentPreview(animated: true)
}
}
从 url 加载 HTML5
页面时,我在该页面某处获取 pdf,我必须下载该 pdf 或将其另存为 base64
。
这是 pdf 在 HTML 代码中的位置。我不能简单地点击 'src' URL 并获取 pdf。
< embed width="100%" height="100%" name="plugin" id="plugin" src="https://myurl.com/fileToOpen.pdf” type="application/pdf" internalinstanceid="8" title="">
任何 JS 可以帮助我获取 base64
字符串或任何其他下载方法?
PS: 使用答案作为评论,因为我需要格式化
您应该在 webview
中执行以下 JavaScriptpath = document.getElementById("plugin").src
fetch(path).then(function (response) {
response.body.getReader().read().then(function(result) {
return btoa(String.fromCharCode.apply(null, result.value));
}).then(function(b64) {
window.pdf_data = b64;
});
});
然后您可以执行另一个查询来访问 window.pdf_data
假设从 javascript 执行中获取 return 值是可能的?
您是要将 PDF 下载到您的 iPhone 还是 Mac?通常,无法将 PDF 直接下载到您的 iPhone,因为 iPhone 本身不具备存储 PDF 的功能。您需要有 iBooks 或 iCloud Drive,然后在另一个 window 中打开 PDF,然后手动下载它。下载 PDF 之前仍然需要用户交互,这意味着用户必须批准下载。通过将 JavaScript 注入到 WKWebView
实例中直接下载是不可能的。
更新
从Docs他们说
The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest
您还可以使用以下字符串从 WKWebview 中获取 base64 字符串
let s = "path = document.getElementById(\"plugin\").src\n" +
"\n" +
"fetch(path).then(function (response) {\n" +
" response.body.getReader().read().then(function(result) {\n" +
" return btoa(String.fromCharCode.apply(null, result.value));\n" +
" }).then(function(b64) {\n" +
" window.webkit.messageHandlers.myInterface.postMessage(b64);\n" +
" });\n" +
"});"
fetch 和 xmlhttp 都是异步工作的。您需要做的就是等待处理完成后使用 javascript 到 ios 的桥将其传递给 Swift( WKScriptMessageHandler)
使用以下代码获取从javascript到Swift的base64字符串。 我正在使用 WKScriptMessageHandler 从 Javascript 获取回调,当 base64 字符串准备好被使用时 。在 String 中,你只需要传递 pdf 的 url 它会做一个 ajax 请求来获取 pdf 文件,然后将其转换为 base64 字符串。
import UIKit
import WebKit
class ViewController: UIViewController {
@IBOutlet weak var btnPDF: UIButton!
@IBOutlet weak var webViewParentView: UIView!
var activityIndicator: UIActivityIndicatorView?
var webView: WKWebView!
@objc func didSelect(_ sender: UIView){
let s="var xhr = new XMLHttpRequest();\n" +
"xhr.open(\'GET\', \"https://codingexceptions.com/wkwebview/dummy.pdf\", true);\n" +
"\n" +
"xhr.responseType = \'arraybuffer\';\n" +
"\n" +
"xhr.onload = function(e) {\n" +
" if (this.status == 200) {\n" +
" var uInt8Array = new Uint8Array(this.response);\n" +
" var i = uInt8Array.length;\n" +
" var binaryString = new Array(i);\n" +
" while (i--)\n" +
" {\n" +
" binaryString[i] = String.fromCharCode(uInt8Array[i]);\n" +
" }\n" +
" var data = binaryString.join(\'\');\n" +
"\n" +
" var base64 = window.btoa(data);\n" +
"\n" +
"window.webkit.messageHandlers.myInterface.postMessage(base64);" +
"\n" +
" }\n" +
"};\n" +
"\n" +
"xhr.send();\n"
webView.configuration.userContentController.add(self, name: "myInterface")
webView?.evaluateJavaScript(s, completionHandler: {(string,error) in
print(error ?? "no error")
})
}
func setupWebView(){
webView = WKWebView.init(frame: CGRect(x: 0, y: 0, width: webViewParentView.frame.width, height: webViewParentView.frame.height))
webView.navigationDelegate = self
webViewParentView.addSubview(webView)
activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicator?.center = self.view.center
self.view.addSubview(activityIndicator!)
webView.load(URLRequest(url: URL(string: "https://codingexceptions.com/wkwebview/index.php")!))
activityIndicator?.startAnimating()
}
override func viewDidLoad() {
super.viewDidLoad()
btnPDF.addTarget(self, action: #selector(self.didSelect(_:)), for: .touchUpInside)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupWebView()
}
}
extension ViewController: WKScriptMessageHandler{
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print("Message received: \(message.name) with body: \(message.body)")
}
}
extension ViewController: WKNavigationDelegate{
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.activityIndicator?.stopAnimating()
self.activityIndicator?.removeFromSuperview()
self.activityIndicator = nil
}
}
更新:按照@Tarun 的回答
从嵌入标签中获取源代码只需将下面的 行放在字符串变量 s 的开头,并在 xhr.open
中传递 urlvar url = document.getElementById("plugin").src
这个问题有时会反问, 但是,如果有人正在寻找 swift 解决方案 WKWebView 以下载 .pdf 或文件管理器上的任何文件,这就是我结束的方式-向上
class WebPortalVC: UIViewController, WKNavigationDelegate,WKUIDelegate, UIDocumentInteractionControllerDelegate,URLSessionDownloadDelegate {
覆盖以下函数,它将拦截 url,在我们的例子中,我们检查以 .pdf 和 .csv 结尾的 ulr 并重定向以使用文件管理器视图打开。启用查看文件、下载并保存到设备存储、空投或与其他应用程序共享
只需添加以下功能并检查即可。
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url {
print("fileDownload: check :: \(url)")
let extention = "\(url)".suffix(4)
if extention == ".pdf" || extention == ".csv"{
print("fileDownload: redirect to download events. \(extention)")
DispatchQueue.main.async {
self.downloadPDF(tempUrl: "\(url)")
}
decisionHandler(.cancel)
return
}
}
decisionHandler(.allow)
}
func downloadPDF(tempUrl:String){
print("fileDownload: downloadPDF")
guard let url = URL(string: tempUrl) else { return }
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
let downloadTask = urlSession.downloadTask(with: url)
downloadTask.resume()
//showHUD(isShowBackground: true); //show progress if you need
}
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
print("fileDownload: documentInteractionControllerViewControllerForPreview")
return self
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// create destination URL with the original pdf name
print("fileDownload: urlSession")
guard let url = downloadTask.originalRequest?.url else { return }
print("fileDownload: urlSession \(url)")
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destinationURL = documentsPath.appendingPathComponent(url.lastPathComponent)
// delete original copy
try? FileManager.default.removeItem(at: destinationURL)
// copy from temp to Document
do {
try FileManager.default.copyItem(at: location, to: destinationURL)
myViewDocumentsmethod(PdfUrl:destinationURL)
print("fileDownload: downloadLocation", destinationURL)
DispatchQueue.main.async {
NBMaterialToast.showWithText(self.view, text: "Download Completed", duration: NBLunchDuration.long)
}
} catch let error {
print("fileDownload: error \(error.localizedDescription)")
}
// dismissHUD(isAnimated: false); //dismiss progress
}
func myViewDocumentsmethod(PdfUrl:URL){
print("fileDownload: myViewDocumentsmethod \(PdfUrl)")
DispatchQueue.main.async {
let controladorDoc = UIDocumentInteractionController(url: PdfUrl)
controladorDoc.delegate = self
controladorDoc.presentPreview(animated: true)
}
}