【问题标题】:Download embedded PDF loaded in WKWebView下载 WKWebView 中加载的嵌入式 PDF
【发布时间】:2018-02-02 17:30:53
【问题描述】:

从 url 加载 HTML5 页面时,我在该页面的某处获取 pdf,我必须下载该 pdf 或将其保存为 base64

这是 pdf 在 HTML 代码中的位置。我不能简单地点击“src”网址并获取 pdf。

< embed width="100%" height="100%" name="plugin" id="plugin" src="https://myurl.com/fileToOpen.pdf” type="application/pdf" internalinstanceid="8" title="">

任何可以帮助我获取base64字符串或任何其他方法下载的JS?

【问题讨论】:

  • 你能在 webview 中执行一段 javascript 代码吗?
  • @TarunLalwani 是的。
  • 你能解释更多吗?或提供您尝试加载的网页的图片?
  • @Nitesh 请看看我的回答

标签: javascript ios swift wkwebview


【解决方案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文件中。
    猜你喜欢
    • 2019-12-27
    • 2010-11-11
    • 2022-10-15
    • 2016-11-28
    • 1970-01-01
    • 2023-03-25
    • 2016-02-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多