下载 WKWebView 中加载的嵌入式 PDF
Posted
技术标签:
【中文标题】下载 WKWebView 中加载的嵌入式 PDF【英文标题】:Download embedded PDF loaded in WKWebView 【发布时间】:2018-02-02 17:30:53 【问题描述】:从 url 加载 html5
页面时,我在该页面的某处获取 pdf,我必须下载该 pdf 或将其保存为 base64
。
这是 pdf 在 HTML 代码中的位置。我不能简单地点击“src”网址并获取 pdf。
< embed name="plugin" id="plugin" src="https://myurl.com/fileToOpen.pdf” type="application/pdf" internalinstanceid="8" title="">
任何可以帮助我获取base64
字符串或任何其他方法下载的JS?
【问题讨论】:
你能在 webview 中执行一段 javascript 代码吗? @TarunLalwani 是的。 你能解释更多吗?或提供您尝试加载的网页的图片? @Nitesh 请看看我的回答 【参考方案1】:这个问题有时会被问到, 但是,如果有人使用 WKWebView 寻找 swift 解决方案来下载 .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)
【讨论】:
【参考方案2】:更新
他们说来自Docs
Fetch API 提供了一个用于获取资源的接口(包括 通过网络)。用过的人都会觉得很熟悉 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 的桥 (WKScriptMessageHandler) 将其传递给 Swift
使用以下代码将 base64 字符串从 javascript 获取到 Swift。 我正在使用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 的答案中指出的 embed 标记获取源代码
只需将下面的 行放在字符串变量 s 的开头 并在 xhr.open 中传递 url
var url = document.getElementById("plugin").src
【讨论】:
@Nitesh 很高兴这有效。这能解决您当前的需求吗? 是的。你也可以帮我解决一下 Tarun 的解决方案。或者如果可能的话也更新答案。 @SahilManchanda,这是我不知道的部分,因为它涉及 iOS 代码。感谢您的更新:-) @SahilManchanda 你能帮忙吗?现在突然上面的脚本停止工作了。网站 js 中唯一的变化是 internalinstanceid="4" 之前是 8。 【参考方案3】:PS:使用答案作为 cmets,因为我需要格式化
你应该在 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 执行中获取返回值是可能的?
【讨论】:
上面的代码不工作或者我不明白如何让它工作。例如,这就是我获取图像的 base64 字符串的方式,它可以帮助您指导我。 var c = document.createElement('canvas'); var ctx = c.getContext('2d'); ctx.drawImage(document.getElementById('captcha_id'), 100, 40); var 值 = c.toDataURL(); value.split(',')[1]; 如何从 swift 代码中获取价值?抱歉我不是IOS开发者 'window.pdf_data = b64;'这个 b64 是字符串吗? 是的,这将包含 base64 字符串。但请记住,这是异步代码。所以该值不会立即可用 我也在用 Sahil 提供的答案尝试这种方法。【参考方案4】:您是否希望将 PDF 下载到您的 iPhone 或 Mac 上?通常,无法将 PDF 直接下载到 iPhone,因为 iPhone 本身不具备存储 PDF 的功能。您需要拥有 iBooks 或 iCloud Drive,然后在另一个窗口中打开 PDF,然后手动下载。在您下载 PDF 之前仍然需要进行用户交互,这意味着用户必须批准下载。无法通过将 JavaScript 注入 WKWebView
实例直接下载。
【讨论】:
如果我没记错的话,我们也可以将其存储在 Documents 中。关于直接下载,是的,即使我开始认为这是不可能的。只是想尝试一下,因为我不是 JS 的 gd。 文档,你的意思是在 Mac 上? iPhone 上没有文档应用。您可以尝试使用 Google Drive 应用程序,但这与使用 iBooks 或 iCloud Drive 的过程相同。 可以将pdf直接下载到iPhone文件中。以上是关于下载 WKWebView 中加载的嵌入式 PDF的主要内容,如果未能解决你的问题,请参考以下文章