【问题标题】:How to handle blob url in WKWebView and download file?如何处理 WKWebView 中的 blob url 并下载文件?
【发布时间】:2021-09-02 11:58:44
【问题描述】:

我需要在 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

返回不支持的 URL 错误。

我尝试像在 android (Example for android) 中一样处理和下载 blob url:

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 未调用。 它调用xhr.onerror,状态为0,responseText为空。

我还尝试从 blob url 中删除“blob:”。 "blob:http://example.com/bd0ae6c8-796c-488e-b691-cee86fa72f04" -> "http://example.com/bd0ae6c8-796c-488e-b691-cee86fa72f04" 并使用 URLSession 下载它。但它让我损坏了文件。

我尝试添加 LSApplicationQueriesSchemes blob,但它对我没有帮助。

【问题讨论】:

  • 您收到的错误是什么?我看到UrL是http。您是否允许任意负载进行非 TLS 连接?
  • @CloudBalancing ,是的,我做到了。我更新了我的问题。如果您询问 XMLHttpRequest,那么它会调用状态为 0 且没有 responseText 的 onerror。
  • 我的意思是您在尝试使用 url 会话下载 blob 时遇到的错误。您说 iOs >= 14.5 的代码有效。意味着 url 会话失败?
  • 在 iOS >= 14.5 上它可以工作,因为 Apple 处理它并使用 decisionHandler(.download) 和下载委托自动下载。但是在 iOS
  • 好的。我应该更多地阅读它,我认为您将不得不在网页中处理一些 JS。似乎 WKWebView 直到最近才支持开箱即用的 blob-stackoverflow.com/questions/61702414/…。您将不得不转换为数据 uri - stackoverflow.com/questions/14952052/…

标签: ios swift blob wkwebview bloburls


【解决方案1】:

也许您应该在孤立的世界中评估 JavaScript。对于 iOS,您应该评估 defauliClientWorld 中的 js 代码。

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
猜你喜欢
  • 2020-08-25
  • 1970-01-01
  • 1970-01-01
  • 2022-10-25
  • 2019-03-25
  • 2019-11-29
  • 1970-01-01
  • 2011-06-04
  • 2019-11-05
相关资源
最近更新 更多