自动将 HTML <img> 从 WKWebView 保存到应用程序的 tmp 目录

Posted

技术标签:

【中文标题】自动将 HTML <img> 从 WKWebView 保存到应用程序的 tmp 目录【英文标题】:Automatically save a HTML <img> from a WKWebView to app's tmp directory 【发布时间】:2018-10-29 09:08:08 【问题描述】:

我有一个可以访问任意网站的 WKWebView。当用户单击任何 html &lt;img&gt; 元素时,我希望它将该图像(作为文件)透明地保存到应用程序 tmp 目录中,理想情况下使用标准标题(img.png),并且理想情况下它每次都会覆盖。

鉴于客户端 javascript 无法访问文件系统,我预计全自动解决方案将涉及 FileManager;但是,我不知道如何将&lt;img&gt; 数据从WKWebView 传输到FileManager 实例。我想知道是否可能需要涉及JavaScriptCore,以在两者之间架起数据桥梁。

我看到 semi-automatic solutions exist,通过使用 HTML download 属性,其中提示用户“另存为...”对话框。这并不理想,因为我希望该操作是透明且没有用户错误。但是,它可能被证明是唯一的选择。

我在 macOS 和 ios 上都实现了这个,所以我可以接受任何一个平台的解决方案;我预计两者之间不会有什么不同。

【问题讨论】:

如果您正在访问的 Web URL 没有提供下载图像的权限,那么我认为没有办法 WKWebView 只是加载网页WKWebView内的所有交互都取决于该网页内用于交互的用户脚本,我们不在WKWebView 内有任何delegate,用于跟踪用户与网页内的HTML 标签 的交互 我已经设置了一个 JavaScript 点击处理程序,用于识别用户是否点击了 HTML &lt;img&gt; 元素。我可以使用这个回调本地;问题在于调用什么代码。 您将如何从您的网页向您的应用程序发送一条用户已点击某个图像的消息? 您可以检查这些委托方法:developer.apple.com/documentation/webkit/wknavigationdelegate @ShubhamBakshi 我正在使用WKScriptMessageHandler 从 WKWebView 与本机应用程序进行通信。我通过WKUserContentController 将脚本注入到 WKWebView 页面中——其中一个脚本就是我提到的附加点击处理程序的脚本。 WKNavigationDelegate 在这里不太相关。 【参考方案1】:

我可以提出以下建议吗:

    准备好您的视图控制器以接收来自 WKWebView 的 javascript 端的消息。我通常在视图控制器的viewDidLoad 中这样做。 在网页中加载并执行一个 javascript,它将为每个 img 标记添加一个 onClick 事件。 在这种情况下,您将使用图像数据的 Base64 编码字符串作为参数从 javascript 向 Objective-C / Swift 端发送回消息 在消息的 Objective-C / Swift 处理程序中,将该字符串转换为数据并保存。

第 1 步和第 2 步:

- (void)    viewDidLoad

    [super viewDidLoad] ;
    WKUserContentController *controller = self.webView.configuration.userContentController ;

    //  Add self as scriptMessageHandler of the webView to receive messages from the scripts
    [controller addScriptMessageHandler:self
                                   name:@"imageHasBeenClicked"] ;

    //  Load script
    NSURL       *scriptURL      = <<... URL of your javascript (can be bundled in your app) ...>> ;
    NSString    *scriptString   = [NSString stringWithContentsOfURL:scriptURL
                                                           encoding:NSUTF8StringEncoding
                                                              error:NULL] ;
    WKUserScript    *script = [[WKUserScript alloc] initWithSource:scriptString
                                                     injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                  forMainFrameOnly:YES] ;
    [controller addUserScript:script] ;

第 3 步:

javascript:

//  This function takes an image tab and encodes the image as BAse64
function getBase64Image(img)

    // Create an empty canvas element
    var canvas      = document.createElement("canvas") ;
    canvas.width    = img.width;
    canvas.height   = img.height;

    // Copy the image contents to the canvas
    var ctx         = canvas.getContext("2d") ;
    ctx.drawImage(img,0,0) ;

    // Get the data-URL formatted image, use PNG as JPG re-encode the image
    var dataURL     = canvas.toDataURL("image/png");

    //  Remove the initial marker so that we directly have NSData compatibility
    return dataURL.replace(/^data:image\/(png|jpg);base64,/,"");


//  Search for all img tags and add an onclick event that will encode the image
//  then, send it to the objective-c side
var imgList = document.getElementsByTagName("img") ;
for (var i = 0; i < imgList.length; i++)

    imgList[i].onclick = function()
    
        var txt = getBase64Image(this) ;
        window.webkit.messageHandlers["imageHasBeenClicked"].postMessage(txt) ;
     ;

第 4 步:

当视图控制器收到“imageHasBeenCLicked”消息后,将Base64字符串转换为数据,保存为图片文件。

- (void)    userContentController:(WKUserContentController*)userContentController
      didReceiveScriptMessage:(WKScriptMessage*)message

    if ([message.name isEqualToString:@"imageHasBeenClicked"])
    
        NSData *data    = [[NSData alloc] initWithBase64EncodedString:message.body
                                                          options:0] ;
       [data writeToFile:@"/toto.png"
              atomically:YES] ;
    

【讨论】:

这种方法(虽然可行)意味着重新下载已经下载的图像。由于&lt;img&gt; 数据已经加载到内存中,我的目标是将浏览器的现有数据副本保存到磁盘。如问题所述,我的目标是“保存”图像;不要“下载”它。很抱歉有歧义。 是的,同意。所以我修改了我的答案以避免重新加载图像。我检查了一个中间 Base64 编码的字符串,该字符串发送到 Objective-C 端。 不幸的是,尽管跨域安全,该解决方案仍需要工作(这就是为什么我指定保存图像而不是任何涉及下载的内容)。例如,在this comment 中的主&lt;img&gt; 元素上运行getBase64Image() 会因调用canvas.toDataURL() 而被SecurityError 中断。

以上是关于自动将 HTML <img> 从 WKWebView 保存到应用程序的 tmp 目录的主要内容,如果未能解决你的问题,请参考以下文章

html视频自动播放

TSQL 从 HTML 中删除带有特定 src 的 img 标签

我无法将 DOM 从 <img> 遍历到 <td> ...?

如何将 URL 从 Webapi 传递到 <img> 标签以接受 base64 的图像?

如何从 img 获取 RGB 数组?

将 <img> HTML 标记添加到 C# 字符串