如何在WKWebView中处理Blob URL并下载文件?

3

我需要在WKWebView中实现文件下载。

我的示例实现:

@available(iOS 14.5, *)
func webView(_ webView: WKWebView, navigationResponse: WKNavigationResponse, didBecome download: WKDownload) {
    download.delegate = self
}
@available(iOS 14.5, *)
func webView(_ webView: WKWebView, navigationAction: WKNavigationAction, didBecome download: WKDownload) {
    download.delegate = self
}
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
    if let mimeType = navigationResponse.response.mimeType {
        if let url = navigationResponse.response.url {
            if #available(iOS 14.5, *) {
                decisionHandler(.download)
            } else {
                let ext = getExtension(mimeType)
                let fileName = "file." + ext;
                downloadData(fromURL: url, fileName: fileName) { success, destinationURL in
                    if success, let destinationURL = destinationURL {
                        self.openDownloadFile(destinationURL)
                    }
                }
                decisionHandler(.cancel)
            }
            return
        }
    }
    decisionHandler(.allow)
}

private func downloadData(fromURL url: URL,
                          fileName: String,
                          completion: @escaping (Bool, URL?) -> Void) {
    webView.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
        let session = URLSession.shared
        session.configuration.httpCookieStorage?.setCookies(cookies, for: url, mainDocumentURL: nil)
        let task = session.downloadTask(with: downloadURL) { localURL, _, error in
            if let localURL = localURL {
                let destinationURL = self.moveDownloadedFile(url: localURL, fileName: fileName)
                completion(true, destinationURL)
            } else {
                completion(false, nil)
            }
        }
        task.resume()
    }
}

private func moveDownloadedFile(url: URL, fileName: String) -> URL {
    let tempDir = NSTemporaryDirectory()
    let destinationPath = tempDir + fileName
    let destinationURL = URL(fileURLWithPath: destinationPath)
    try? FileManager.default.removeItem(at: destinationURL)
    try? FileManager.default.moveItem(at: url, to: destinationURL)
    return destinationURL
}

private func openDownloadFile(_ fileUrl: URL) {
    DispatchQueue.main.async {
        let controller = UIActivityViewController(activityItems: [fileUrl], applicationActivities: nil)
        controller.popoverPresentationController?.sourceView = self.view
        controller.popoverPresentationController?.sourceRect = self.view.frame
        controller.popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
        self.present(controller, animated: true, completion: nil)
    }
}

它适用于iOS >= 14.5以及响应为文件的情况。但是当url为blob(例如"blob:http://example.com/bd0ae6c8-796c-488e-b691-cee86fa72f04")时,它在iOS < 14.5上无法正常工作。

返回不支持的URL错误。

我尝试像在Android中一样处理和下载blob url (Android示例):

private func downloadBlobFile(blobUrl: String, fileName: String, mimeType: String) {
    webView.evaluateJavaScript("javascript: var xhr = new XMLHttpRequest();" +
        "xhr.open('GET', '" + blobUrl + "', true);" +
        "xhr.setRequestHeader('Content-type','" + mimeType + "');" +
        "xhr.responseType = 'blob';" +
        "xhr.onload = function(e) {" +
        "    if (this.status == 200) {" +
        "        var blob = this.response;" +
        "        var reader = new FileReader();" +
        "        reader.readAsDataURL(blob);" +
        "        reader.onloadend = function() {" +
        "            base64data = reader.result;" +
        "            window.webkit.messageHandlers.callback.postMessage(base64data);" +
        "        }" +
        "    }" +
        "};" +
        "xhr.send();")
}

这段代码在Android上运行良好,但是在iOS上并没有调用xhr.onload,而是用空的responseText调用了xhr.onerror

我还尝试去掉blob url中的"blob:"。 "blob:http://example.com/bd0ae6c8-796c-488e-b691-cee86fa72f04" -> "http://example.com/bd0ae6c8-796c-488e-b691-cee86fa72f04",然后使用NSURLSession下载。 但是它给我下载了一个损坏的文件。

另外,我尝试添加LSApplicationQueriesSchemes blob,但无济于事。 enter image description here


你收到了什么错误?我看到URL是http。你允许任意加载以进行非TLS连接了吗? - CloudBalancing
@CloudBalancing,是的,我做到了。我更新了我的问题。如果你问的是XMLHttpRequest,那么它会调用onerror并返回状态0,没有responseText。 - runia
我指的是在尝试使用URL会话下载Blob时出现的错误。您说iOs >= 14.5的代码可以工作。这意味着URL会话失败了吗? - CloudBalancing
在iOS >= 14.5上,它可以工作,因为苹果会自动处理并使用decisionHandler(.download)和下载委托进行下载。但是在iOS <14.5上需要手动处理和下载文件。当我尝试使用URLSession下载blob时,会出现不支持的URL错误。 - runia
好的。我应该多读一点,我觉得你将不得不在网页中处理一些JS。最近WKWebView似乎还不支持"blob",直到最近-https://stackoverflow.com/questions/61702414/wkwebview-how-to-handle-blob-url。你将不得不转换为数据URI-https://dev59.com/OGUp5IYBdhLWcg3w6q2q#16179887 - CloudBalancing
显示剩余3条评论
1个回答

0

也许你应该在隔离的环境中评估 JavaScript。对于 iOS,你应该在默认客户端环境中评估 js 代码。


1
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接