IOS - 如何从 WKWebView 获取缓存的资源?

Posted

技术标签:

【中文标题】IOS - 如何从 WKWebView 获取缓存的资源?【英文标题】:IOS - How to get cached resources from WKWebView? 【发布时间】:2018-11-21 18:52:45 【问题描述】:

据我所知,WKWebView 中加载的所有资源的缓存是默认设置的。有几篇关于How to remove cached resources from WKWebView?的帖子,但我找不到任何关于从WKWebView获取它的方法的帖子。

例如,当我使用WKWebView 加载this url 时,它会显示一个pdf 文件,我想要在url 完全加载后从WKWebView 获取这个pdf 文件以共享

我已经检查了 Chrome,如果我想从上面的链接共享文件,它会在显示共享对话框后再次从 url 请求和下载内容。这意味着他们没有从当前的WKWebView(缓存资源)中得到它。

但在 Safari 上,不知何故,它们会在单击共享按钮后立即显示共享对话框,并且似乎无需再次下载。我认为他们从缓存或WkWebView 的某个地方获取 pdf 文件并分享它。

理解问题的另一种方式是如何在不再次下载内容的情况下获取显示在 WKWebView 上的文件?

如果问题没有足够的信息来回答,请随时发表评论,我会更清楚地说明。

【问题讨论】:

你找到解决办法了吗? @Mrug Marked answer提供了一个可能的解决方案,你可以看看。 我试过了,但它的 PDF 有一个问题,它的 URL 带有一些与安全相关的标题。 ***.com/questions/51871789/… 【参考方案1】:

据我所知,WKWebView 中加载的所有资源的缓存由 默认值:

请记住,如果您请求相同的资源,webview 将不会从 Internet 加载内容,它会为您提供缓存资源中的内容。对于请求相同的资源,您可以使用一些 JavaScript 来获取内容。

看看下面的代码。加载 PDF 并点击保存按钮后。它将执行 javascript 代码,并在数据准备好通过 JavaScript 传递时

它会触发window.webkit.messageHandlers.myInterface.postMessage(base64)

让您的 ViewController 知道数据已准备好共享。

您可以通过以下方式进行验证

    让它加载 PDF

    关闭模拟器的网络(see)

    点击保存按钮

您将获得 base64 格式的 pdf 数据。保存并分享它:)

    import UIKit
    import WebKit
    class ViewController: UIViewController 

        @IBOutlet weak var webView: WKWebView!
        var activityIndicator: UIActivityIndicatorView?
        override func viewDidLoad() 
            super.viewDidLoad()

        

        override func viewDidAppear(_ animated: Bool) 
            super.viewDidAppear(animated)
            activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
            webView.navigationDelegate = self
            activityIndicator?.center = self.view.center
            self.view.addSubview(activityIndicator!)
            webView.configuration.userContentController.add(self, name: "myInterface")
            webView.load(URLRequest(url: URL(string: "http://www.africau.edu/images/default/sample.pdf")!))
            activityIndicator?.startAnimating()
        

        @IBAction func saveAction(_ 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) 
    //    print("Message received: \(message.name) with body: \(message.body)")

        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(documentsURL)

        let activityViewController = UIActivityViewController.init(activityItems: [documentsURL], applicationActivities: nil)
        present(activityViewController, animated: true, completion: nil)
      
    

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

顺便说一句,您提供的 pdf 链接使用的是 HTTP 而不是 HTTPS。因此,出于测试目的,在您的 info.plist

中添加以下内容
<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoadsInWebContent</key>
        <true/>
    </dict>

【讨论】:

太棒了!!!似乎这是我所期望的。我会检查您的代码并尽快通知您。非常感谢。 @trungduc 很好地补充了答案 注入不适用于所有情况。 Blocked script execution in 'URL' because the document's frame is sandboxed and the 'allow-scripts' permission is not set. @Mrug 你在使用 IFRAME 吗? 不。 iframe 不存在,但加载 PDF 的上一页可能有 iframe。

以上是关于IOS - 如何从 WKWebView 获取缓存的资源?的主要内容,如果未能解决你的问题,请参考以下文章

从 WKWebView 获取 favicon.co

登录 ios swift 后从 wkwebview 获取令牌

如何获取 WKWebView 上指定位置的信息?

iOS - WKWebView - 在后续应用程序启动期间缓存应用程序的先前状态

如何在 Swift 4 中从 WKWebView 获取 cookie 值?

清除WKWebView的缓存