从Blob WKWebView下载图像而无需知道url

时间:2018-10-21 22:01:38

标签: javascript ios swift uiwebview wkwebview

我正在使用WKWebView访问一个网站,该网站上有一个下载按钮。在使用下载文件的便携式计算机的Chrome上,当我单击iOS模拟器上的“下载”时,它将在WKWebView中显示图像。我的目标是保存该图像并将其稍后显示在CollectionView中。如果我将URL设置为https://www.website.com/image.jpg,那将很容易做到,问题在于它是blob url,因此格式为blob:https://www.website.com/abcd-efgh-ijkl

我发现此链接有所帮助: How to read a blob data URL in WKWebView?

这是我的ViewController的精简版:

import UIKit
import WebKit

class DownloadBlob: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {

    lazy var webView: WKWebView = {
        let config = WKWebViewConfiguration()
        config.userContentController.add(self, name: "readBlob")
        let webView = WKWebView(frame: UIScreen.main.bounds, configuration: config)
        webView.navigationDelegate = self
        return webView
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        let request = URLRequest(url: url!)
        webView.load(request)
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        print("Finished")
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        print(navigationResponse)

        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        print(navigationAction)
        let navigationURL = navigationAction.request.url
        if (navigationURL?.absoluteString.contains("blob"))! {
            testDownloadBlob(navigationURL: navigationURL!)
        }
        decisionHandler(.allow)
    }

    func testDownloadBlob(navigationURL: URL) {

        var script = ""
        script = script + "var xhr = new XMLHttpRequest();"
        script = script + "xhr.open('GET', '\(navigationURL.absoluteString)', true);"
        script = script + "xhr.responseType = 'blob';"
        script = script + "window.webkit.messageHandlers.readBlob.postMessage('making sure script called');"
        script = script + "xhr.onload = function(e) { if (this.status == 200) { var blob = this.response; window.webkit.messageHandlers.readBlob.postMessage(blob); var reader = new window.FileReader(); reader.readAsBinaryString(blob); reader.onloadend = function() { window.webkit.messageHandlers.readBlob.postMessage(reader.result); }}};"
        script = script + "xhr.send();"

        self.webView.evaluateJavaScript(script) { (results, error) in
            print(results ?? "")
        }
    }

    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print(message.name, message.body)
    }


}

我注意到根本没有调用xhr.onload,但是不确定如何解决此问题。但是,我的确在果盘中获得了一个响应,而该果盘在DecisionPolicyFor navigationResponse中。

有没有一种方法可以获取图像数据并保存?

1 个答案:

答案 0 :(得分:0)

此链接包含使用datauri的完整解决方法。重要的部分是使用访存获取blob并将其转换为datauri。 https://forums.developer.apple.com/thread/108394#333195
虽然,我看不出OP的二进制字符串postMessage不起作用的任何原因。如果可以,它将比使用需要转换为base64的数据uri效率更高。
也许OP只是没有正确挂钩Javascript通信权限,或者XHR无法正常工作,必须使用访存。

下面只是将Javascript注入页面,并通过拦截blob并使用datauris使链接起作用。 请注意,这是一个真正的回旋技术。扫描所有href并将所有检测到的blob网址替换为datauri。 编辑:更新以显示它正在运行

function blobToDataURL(blob, callback) {
    var a = new FileReader();
    a.onload = function(e) {callback(e.target.result);}
    a.readAsDataURL(blob);
}
// not sure what elements you are going to intercept:
document.querySelectorAll('a').forEach(async (el)=>{
   const url = el.getAttribute('href');
   if( url.indexOf('blob:')===0 ) {
       let blob = await fetch(url).then(r => r.blob());
       blobToDataURL(blob, datauri => el.setAttribute('href',datauri));
   }
});

b=new Blob([new Int8Array([1,2,3,4,5,6,7,8,9,10]).buffer]);
test.href=URL.createObjectURL(b);
b=new Blob([new Int8Array([31,32,33,34,35]).buffer]);
test1.href=URL.createObjectURL(b);
b=new Blob([new Int8Array([51,52,53,54]).buffer]);
test2.href=URL.createObjectURL(b);



function blobToDataURL(blob, callback) {
    var a = new FileReader();
    a.onload = function(e) {callback(e.target.result);}
    a.readAsDataURL(blob);
}

document.addEventListener('click', function(event) {
  event.preventDefault();
  if ( event.target.matches('a[href^="blob:"]') )
     (async el=>{
       const url = el.href;
       const blob = await fetch(url).then(r => r.blob());
       blobToDataURL(blob, datauri => el.href=datauri);
     })(event.target);
});

// not sure what elements you are going to intercept:
/*document.querySelectorAll('a').forEach(async (el)=>{
   const url = el.href;
   if( url.indexOf('blob:')===0 ) {
       let blob = await fetch(url).then(r => r.blob());
       blobToDataURL(blob, datauri => el.href=datauri);
   }
});*/
<a id="test">test</a>
<a id="test1">test</a>
<a id="test2">test</a>

点击时进行数据uri转换的示例:

b=new Blob([new Int8Array([1,2,3,4,5,6,7,8,9,10]).buffer]);
test.href=URL.createObjectURL(b);
b=new Blob([new Int8Array([31,32,33,34,35]).buffer]);
test1.href=URL.createObjectURL(b);
b=new Blob([new Int8Array([51,52,53,54]).buffer]);
test2.href=URL.createObjectURL(b);



function blobToDataURL(blob, callback) {
    var a = new FileReader();
    a.onload = function(e) {callback(e.target.result);}
    a.readAsDataURL(blob);
}

document.addEventListener('click', function(event) {
  if ( event.target.matches('a[href^="blob:"]') ) {
     event.preventDefault();
     (async el=>{
       const url = el.href;
       const blob = await fetch(url).then(r => r.blob());
       blobToDataURL(blob, datauri => window.open(el.href=datauri,el.target||'_self'));
     })(event.target);
   }
});

// not sure what elements you are going to intercept:
/*document.querySelectorAll('a').forEach(async (el)=>{
   const url = el.href;
   if( url.indexOf('blob:')===0 ) {
       let blob = await fetch(url).then(r => r.blob());
       blobToDataURL(blob, datauri => el.href=datauri);
   }
});*/
<a id="test">test</a>
<a id="test1">test</a>
<a id="test2">test</a>