【问题标题】:Get already loaded PDF directly from WKWebView without downloading again直接从 WKWebView 获取已加载的 PDF,无需再次下载
【发布时间】:2019-01-23 02:28:52
【问题描述】:

我正在尝试直接在 WKWebView 中加载 PDF,而不向 URL 发出另一个直接请求。

在我的研究过程中,我遇到了下面的链接,它做同样的事情。 IOS - How to get cached resources from WKWebView?

但在这种方法中,我在某些 URL 中遇到了问题。 我在控制台中收到 Javascript 错误 Blocked script execution in 'https://ac.els-cdn.com/S0946672X17308763/1-s2.0-S0946672X17308763-main.pdf?_tid=8d087878-4e4c-4c50-b5e4-08ea38906e6e&download=true&acdnat=1534405212_984aaf5f9673f1b2ff107a4032c80ea0' because the document's frame is sandboxed and the 'allow-scripts' permission is not set.

重现问题的 URL。 https://www.sciencedirect.com/science/article/pii/S0946672X17308763 只需访问下载 PDF 链接并在 WKWebView 中打开 PDF。

为便于在同一个 WebView 中加载新的选项卡窗口而进行编辑。

-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{

    if (!navigationAction.targetFrame.isMainFrame) {
        [_webView loadRequest:navigationAction.request];
    }
    return nil;
}

编辑

我正在添加从@SahilManchanda 提供的解决方案派生的完整 swift 代码,但在代码 sn-p 中添加的链接仍然存在一些问题。

import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!
    var button: UIButton!
    let link = "https://www.sciencedirect.com/science/article/pii/S0946672X17308763"

    var activityIndicator: UIActivityIndicatorView?
    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        webView = WKWebView()
        button = UIButton()
        button.setTitle("Save", for: .normal)
        button.setTitleColor(.white, for: .normal)
        button.backgroundColor = UIColor.black.withAlphaComponent(0.5)
        button.addTarget(self, action: #selector(self.download(_:)), for: .touchUpInside)
        activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        webView.navigationDelegate = self
        webView.uiDelegate = self
        activityIndicator?.center = self.view.center
        [webView,button].forEach({view.addSubview($0)})
        setupConstraints()
        webView.configuration.userContentController.add(self, name: "myInterface")
        webView.load(URLRequest(url: URL(string: link)!))
        activityIndicator?.startAnimating()
    }

    func setupConstraints(){
        webView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            webView.topAnchor.constraint(equalTo: view.topAnchor),
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
        button.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -70),
            button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            button.widthAnchor.constraint(equalToConstant: 100)
            ])
    }
    @objc func download(_ sender: Any){

        let s = """
        var xhr = new XMLHttpRequest();
        xhr.open('GET', "\(webView.url!.absoluteString)", true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function(e) {
        if (this.status == 200) {
        var uInt8Array = new Uint8Array(this.response);
        var i = uInt8Array.length;
        var binaryString = new Array(i);
        while (i--){
        binaryString[i] = String.fromCharCode(uInt8Array[i]);
        }
        var data = binaryString.join('');
        var base64 = window.btoa(data);

        window.webkit.messageHandlers.myInterface.postMessage(base64);
        }
        };
        xhr.send();
        """
        webView?.evaluateJavaScript(s, completionHandler: {(string,error) in
            print(error ?? "no error")
        })
    }
}
extension ViewController: WKScriptMessageHandler{
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard
            var documentsURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last,
            let convertedData = Data.init(base64Encoded: message.body as! String)
            else {
                //handle error when getting documents URL
                return
        }
        //name your file however you prefer
        documentsURL.appendPathComponent("sample.pdf")
        do {
            try convertedData.write(to: documentsURL)
        } catch {
            //handle write error here
        }
        //if you want to get a quick output of where your
        //file was saved from the simulator on your machine
        //just print the documentsURL and go there in Finder
        print("URL for view \(documentsURL.absoluteString)")
        let activityViewController = UIActivityViewController.init(activityItems: [documentsURL], applicationActivities: nil)
        present(activityViewController, animated: true, completion: nil)
    }
}

extension ViewController: WKNavigationDelegate,WKUIDelegate{

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {

        if !(navigationAction.targetFrame != nil && (navigationAction.targetFrame?.isMainFrame)!){
            webView .load(navigationAction.request);
        }

        return nil
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.activityIndicator?.stopAnimating()
        self.activityIndicator?.removeFromSuperview()
        self.activityIndicator = nil
    }
}

【问题讨论】:

  • 点击下载->然后出现弹出窗口->点击下载pdf。它没有在 wkwebview 中自动打开。但是我在 navigationAction 委托中获得了下载链接,例如sciencedirect.com/science/article/pii/S0946672X16303777/…你能指导我如何在webview中加载pdf吗?
  • 根据 WKWebview 文档,如果它设置为 _blank,它通常不会直接打开链接目标,所以这里也发生了同样的事情。如果你想打开它,你必须在-(WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures 中提出另一个请求
  • 或者只使用来自 ScienceDirect 网站的任何直接 PDF URL。它发生在所有有一些标头作为服务器响应的地方。例如sciencedirect.com/science/article/pii/S0946672X17308763/…
  • 已添加代码 sn-p 用于在同一位置加载 PDF。
  • 给我一分钟。让我检查

标签: ios objective-c webview wkwebview


【解决方案1】:

在模拟器上尝试以下代码。直接的 pdf 链接不可用。他们动态生成pdf链接。所以当页面完成加载时,它会拥有最近的 url,调用下载方法会再次请求,因此它会从缓存中保存。

   import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!
    var newWebView: WKWebView?
    let link = "https://www.sciencedirect.com/science/article/pii/S0946672X17308763"
    var d = false
    var activityIndicator: UIActivityIndicatorView?
    override func viewDidLoad() {
        super.viewDidLoad()

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        webView = WKWebView()
        activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        webView.navigationDelegate = self
        webView.uiDelegate = self
        activityIndicator?.center = self.view.center
        [webView].forEach({view.addSubview($0)})
        setupConstraints()
        webView.configuration.userContentController.add(self, name: "myInterface")
        webView.load(URLRequest(url: URL(string: link)!))
        activityIndicator?.startAnimating()
    }

    func setupConstraints(){
        webView.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            webView.topAnchor.constraint(equalTo: view.topAnchor),
            webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            webView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
            ])
    }
    func download(_ sender: Any){
        guard let link = (sender as? URL)?.absoluteString else {return}
        print("\(link)")
        let s = """
        var xhr = new XMLHttpRequest();
        xhr.open('GET', "\(link)", true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function(e) {
        if (this.status == 200) {
        var uInt8Array = new Uint8Array(this.response);
        var i = uInt8Array.length;
        var binaryString = new Array(i);
        while (i--){
        binaryString[i] = String.fromCharCode(uInt8Array[i]);
        }
        var data = binaryString.join('');
        var base64 = window.btoa(data);

        window.webkit.messageHandlers.myInterface.postMessage(base64);
        }
        };
        xhr.send();
        """
        webView?.evaluateJavaScript(s, completionHandler: {(string,error) in
            print(error ?? "no error")
        })
    }
}
extension ViewController: WKScriptMessageHandler{
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard
            var documentsURL = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last,
            let convertedData = Data.init(base64Encoded: message.body as! String)
            else {
                //handle error when getting documents URL
                return
        }
        //name your file however you prefer
        documentsURL.appendPathComponent("sample.pdf")
        do {
            try convertedData.write(to: documentsURL)
        } catch {
            //handle write error here
        }
        //if you want to get a quick output of where your
        //file was saved from the simulator on your machine
        //just print the documentsURL and go there in Finder
        print("URL for view \(documentsURL.absoluteString)")
        let activityViewController = UIActivityViewController.init(activityItems: [documentsURL], applicationActivities: nil)
        present(activityViewController, animated: true, completion: nil)
    }
}

extension ViewController: WKNavigationDelegate,WKUIDelegate{

    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {

        if !(navigationAction.targetFrame != nil && (navigationAction.targetFrame?.isMainFrame)!){
            webView .load(navigationAction.request);

        }

        return nil
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        self.activityIndicator?.stopAnimating()
        self.activityIndicator?.removeFromSuperview()
        self.activityIndicator = nil
        if d{
            download(webView.url)
        }
    }
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        print("url \(navigationAction.request.url!.absoluteString)")
        if navigationAction.request.url!.absoluteString.contains("pdfft"){
            if !d{
                d = true
                let url = navigationAction.request.url?.absoluteString.components(separatedBy: "?").first ?? ""
                decisionHandler(.cancel)
                webView.load(URLRequest(url: URL(string: url)!))
                return
            }

        }
        decisionHandler(.allow)
    }
}

页面完成加载后。单击保存按钮。然后它应该打印如下内容:

查看网址 file:///Users/jingged/Library/Developer/CoreSimulator/Devices/B57CEFE8-2A2A-484B-AB36-58E05B68EB1A/data/Containers/Data/Application/17317462-D36C-40EA-AEBD-0F128DC7E66A/Documents/sample.pdf

复制网址并粘贴到任何浏览器中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-27
    • 1970-01-01
    • 2010-09-29
    • 2020-02-09
    • 1970-01-01
    • 2017-12-17
    • 1970-01-01
    相关资源
    最近更新 更多