cod*_*e24 5 swift wkwebview alamofire
我正在尝试快速使用 WKWebView,目前有一个使用 AlamoFire 的下载引擎。我遇到过一个使用 blob: url 方案下载项目的网站。有没有一种方法可以使用 AlamoFire 或 WKWebView 下载 blob 文件?
我的具体目标是将此 blob URI 中的内容下载到文件中。
我将不胜感激任何帮助。谢谢。
所有相关代码附在下面。
这是我遇到问题的 URL:
blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094
Run Code Online (Sandbox Code Playgroud)
这是我的日志中的错误:
2021-12-10 22:41:45.382527-0500 Asobi[14529:358202] -canOpenURL: failed for URL: "blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094" - error: "This app is not allowed to query for scheme blob"
2021-12-10 22:41:45.474214-0500 Asobi[14529:358357] Task <4B011CC1-60E9-4AAD-98F0-BB6A6D0C92FB>.<1> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, NSErrorFailingURLKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDownloadTask <4B011CC1-60E9-4AAD-98F0-BB6A6D0C92FB>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDownloadTask <4B011CC1-60E9-4AAD-98F0-BB6A6D0C92FB>.<1>, NSUnderlyingError=0x6000017e99b0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}}
2021-12-10 22:41:45.476703-0500 Asobi[14529:358202] [Process] 0x124034e18 - [pageProxyID=6, webPageID=7, PID=14540] WebPageProxy::didFailProvisionalLoadForFrame: frameID=3, domain=WebKitErrorDomain, code=102
Failed provisional nav: Error Domain=WebKitErrorDomain Code=102 "Frame load interrupted" UserInfo={_WKRecoveryAttempterErrorKey=<WKReloadFrameErrorRecoveryAttempter: 0x6000019a88c0>, NSErrorFailingURLStringKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, NSErrorFailingURLKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, NSLocalizedDescription=Frame load interrupted}
Run Code Online (Sandbox Code Playgroud)
这是 WKNavigation 决策策略中我的下载决策处理程序的代码
blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094
Run Code Online (Sandbox Code Playgroud)
这是我的下载数据功能的代码(它使用 AF.download 方法)
2021-12-10 22:41:45.382527-0500 Asobi[14529:358202] -canOpenURL: failed for URL: "blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094" - error: "This app is not allowed to query for scheme blob"
2021-12-10 22:41:45.474214-0500 Asobi[14529:358357] Task <4B011CC1-60E9-4AAD-98F0-BB6A6D0C92FB>.<1> finished with error [-1002] Error Domain=NSURLErrorDomain Code=-1002 "unsupported URL" UserInfo={NSLocalizedDescription=unsupported URL, NSErrorFailingURLStringKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, NSErrorFailingURLKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"LocalDownloadTask <4B011CC1-60E9-4AAD-98F0-BB6A6D0C92FB>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDownloadTask <4B011CC1-60E9-4AAD-98F0-BB6A6D0C92FB>.<1>, NSUnderlyingError=0x6000017e99b0 {Error Domain=kCFErrorDomainCFNetwork Code=-1002 "(null)"}}
2021-12-10 22:41:45.476703-0500 Asobi[14529:358202] [Process] 0x124034e18 - [pageProxyID=6, webPageID=7, PID=14540] WebPageProxy::didFailProvisionalLoadForFrame: frameID=3, domain=WebKitErrorDomain, code=102
Failed provisional nav: Error Domain=WebKitErrorDomain Code=102 "Frame load interrupted" UserInfo={_WKRecoveryAttempterErrorKey=<WKReloadFrameErrorRecoveryAttempter: 0x6000019a88c0>, NSErrorFailingURLStringKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, NSErrorFailingURLKey=blob:https://cubari.moe/87d49857-dfef-4f0f-bb83-db8517fd3094, NSLocalizedDescription=Frame load interrupted}
Run Code Online (Sandbox Code Playgroud)
几天后,我能够弄清楚如何在没有 WKDownloadDelegate 的情况下下载 blob URL。以下代码基于此答案。
需要创建一个消息处理程序来响应 JS 消息。我在makeUIView函数中创建了这个
webModel.webView.configuration.userContentController.add(context.coordinator, name: "jsListener")
Run Code Online (Sandbox Code Playgroud)
在 WKNavigationDelegate 内部,您需要在导航操作中添加此代码。
注意:由于我使用 SwiftUI,所以我的所有变量/模型都位于父类(UIViewRepresentable 协调器)中。
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if let url = navigationAction.request.url, let scheme = url.scheme?.lowercased() {
if scheme == "blob" {
// Defer to JS handling
parent.webModel.executeBlobDownloadJS(url: url)
decisionHandler(.cancel)
} else {
decisionHandler(.allow)
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是请求存储在浏览器内存中的 blob 的 JS。我在一个包装函数中添加了这个 JS,该函数evaluateJavaScript使用 url 进行调用,以保持代码的整洁。
function blobToDataURL(blob, callback) {
var reader = new FileReader()
reader.onload = function(e) {callback(e.target.result.split(",")[1])}
reader.readAsDataURL(blob)
}
async function run() {
const url = "\(url)"
const blob = await fetch(url).then(r => r.blob())
blobToDataURL(blob, datauri => {
const responseObj = {
url: url,
mimeType: blob.type,
size: blob.size,
dataString: datauri
}
window.webkit.messageHandlers.jsListener.postMessage(JSON.stringify(responseObj))
})
}
run()
Run Code Online (Sandbox Code Playgroud)
除了返回的 JS 对象之外,我还必须创建一个可以反序列化 JSON 字符串的结构:
struct BlobComponents: Codable {
let url: String
let mimeType: String
let size: Int64
let dataString: String
}
Run Code Online (Sandbox Code Playgroud)
然后,我获取发送到 WKScriptMessageHandler 的消息并解释它们以保存到文件中。我在这里使用了 SwiftUI 文件移动器,但您可以使用此内容执行任何您想要的操作。
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let jsonString = message.body as? String else {
return
}
parent.webModel.blobDownloadWith(jsonString: jsonString)
}
Run Code Online (Sandbox Code Playgroud)
在我的网络模型中(需要导入 CoreServices):
func blobDownloadWith(jsonString: String) {
guard let jsonData = jsonString.data(using: .utf8) else {
print("Cannot convert blob JSON into data!")
return
}
let decoder = JSONDecoder()
do {
let file = try decoder.decode(BlobComponents.self, from: jsonData)
guard let data = Data(base64Encoded: file.dataString),
let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, file.mimeType as CFString, nil),
let ext = UTTypeCopyPreferredTagWithClass(uti.takeRetainedValue(), kUTTagClassFilenameExtension)
else {
print("Error! \(error)")
return
}
let fileName = file.url.components(separatedBy: "/").last ?? "unknown"
let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let url = path.appendingPathComponent("blobDownload-\(fileName).\(ext.takeRetainedValue())")
try data.write(to: url)
downloadFileUrl = url
showFileMover = true
} catch {
print("Error! \(error)")
return
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2306 次 |
| 最近记录: |