我正在使用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中。
有没有一种方法可以获取图像数据并保存?
答案 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>