swift - 如何在wkwebview中下载文件

标签 swift wkwebview

有人请告诉我如何在iOS wkwebview 中下载文件。我创建了一个 iOS webview 应用程序。在我加载的页面中,它有几个下载选项,但是当我点击下载时什么也没有发生。

注意:我不想创建额外的按钮来下载

最佳答案

您还可以使用 JavaScript 下载您的文件,如 Sayooj's link暗示。

当然,您将自己处理文件下载的代码。

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { ,您将获得要下载的文件 url。

然后用JS下载。

JS调用下载的方法如果成功,你会收到public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {的通知,

然后你可以处理你下载的文件

这有点复杂。使用 JavaScript 下载文件,使用 WKScriptMessageHandler 在原生 Swift 和 JavaScript 之间进行通信。

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {  

    var webView: WKWebView!  
    let webViewConfiguration = WKWebViewConfiguration()  
    override func viewDidLoad() {  
        super.viewDidLoad()  

        // init this view controller to receive JavaScript callbacks  
        webViewConfiguration.userContentController.add(self, name: "openDocument")  
        webViewConfiguration.userContentController.add(self, name: "jsError")  
        webView = WKWebView(frame: yourFrame, configuration: webViewConfiguration)  
    }  

    func webView(_ webView: WKWebView,  
                 decidePolicyFor navigationAction: WKNavigationAction,  
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {  
        let url = navigationAction.request.url  
        decisionHandler(.cancel)  
        executeDocumentDownloadScript(forAbsoluteUrl: url!.absoluteString)  

    }  

    /* 
     Handler method for JavaScript calls. 
     Receive JavaScript message with downloaded document 
     */  
    public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {  
        debugPrint("did receive message \(message.name)")  


        if (message.name == "openDocument") {  
            handleDocument(messageBody: message.body as! String)  
        } else if (message.name == "jsError") {  
            debugPrint(message.body as! String)  
        }  
    }  

    /* 
     Open downloaded document in QuickLook preview 
     */  
    private func handleDocument(messageBody: String) {  
        // messageBody is in the format ;data:;base64,  

        // split on the first ";", to reveal the filename  
        let filenameSplits = messageBody.split(separator: ";", maxSplits: 1, omittingEmptySubsequences: false)  

        let filename = String(filenameSplits[0])  

        // split the remaining part on the first ",", to reveal the base64 data  
        let dataSplits = filenameSplits[1].split(separator: ",", maxSplits: 1, omittingEmptySubsequences: false)  

        let data = Data(base64Encoded: String(dataSplits[1]))  

        if (data == nil) {  
            debugPrint("Could not construct data from base64")  
            return  
        }  

        // store the file on disk (.removingPercentEncoding removes possible URL encoded characters like "%20" for blank)  
        let localFileURL = FileManager.default.temporaryDirectory.appendingPathComponent(filename.removingPercentEncoding ?? filename)  

        do {  
            try data!.write(to: localFileURL);  
        } catch {  
            debugPrint(error)  
            return  
        }  

        // and display it in QL  
        DispatchQueue.main.async {  
            // localFileURL  
            // now you have your file
        }  
    }  



    /* 
     Intercept the download of documents in webView, trigger the download in JavaScript and pass the binary file to JavaScript handler in Swift code 
     */  
    private func executeDocumentDownloadScript(forAbsoluteUrl absoluteUrl : String) {  
        // TODO: Add more supported mime-types for missing content-disposition headers  
        webView.evaluateJavaScript("""  
            (async function download() {  
                const url = '\(absoluteUrl)';  
                try {  
                    // we use a second try block here to have more detailed error information  
                    // because of the nature of JS the outer try-catch doesn't know anything where the error happended  
                    let res;  
                    try {  
                        res = await fetch(url, {  
                            credentials: 'include'  
                        });  
                    } catch (err) {  
                        window.webkit.messageHandlers.jsError.postMessage(`fetch threw, error: ${err}, url: ${url}`);  
                        return;  
                    }  
                    if (!res.ok) {  
                        window.webkit.messageHandlers.jsError.postMessage(`Response status was not ok, status: ${res.status}, url: ${url}`);  
                        return;  
                    }  
                    const contentDisp = res.headers.get('content-disposition');  
                    if (contentDisp) {  
                        const match = contentDisp.match(/(^;|)\\s*filename=\\s*(\"([^\"]*)\"|([^;\\s]*))\\s*(;|$)/i);  
                        if (match) {  
                            filename = match[3] || match[4];  
                        } else {  
                            // TODO: we could here guess the filename from the mime-type (e.g. unnamed.pdf for pdfs, or unnamed.tiff for tiffs)  
                            window.webkit.messageHandlers.jsError.postMessage(`content-disposition header could not be matched against regex, content-disposition: ${contentDisp} url: ${url}`);  
                        }  
                    } else {  
                        window.webkit.messageHandlers.jsError.postMessage(`content-disposition header missing, url: ${url}`);  
                        return;  
                    }  
                    if (!filename) {  
                        const contentType = res.headers.get('content-type');  
                        if (contentType) {  
                            if (contentType.indexOf('application/json') === 0) {  
                                filename = 'unnamed.pdf';  
                            } else if (contentType.indexOf('image/tiff') === 0) {  
                                filename = 'unnamed.tiff';  
                            }  
                        }  
                    }  
                    if (!filename) {  
                        window.webkit.messageHandlers.jsError.postMessage(`Could not determine filename from content-disposition nor content-type, content-dispositon: ${contentDispositon}, content-type: ${contentType}, url: ${url}`);  
                    }  
                    let data;  
                    try {  
                        data = await res.blob();  
                    } catch (err) {  
                        window.webkit.messageHandlers.jsError.postMessage(`res.blob() threw, error: ${err}, url: ${url}`);  
                        return;  
                    }  
                    const fr = new FileReader();  
                    fr.onload = () => {  
                        window.webkit.messageHandlers.openDocument.postMessage(`${filename};${fr.result}`)  
                    };  
                    fr.addEventListener('error', (err) => {  
                        window.webkit.messageHandlers.jsError.postMessage(`FileReader threw, error: ${err}`)  
                    })  
                    fr.readAsDataURL(data);  
                } catch (err) {  
                    // TODO: better log the error, currently only TypeError: Type error  
                    window.webkit.messageHandlers.jsError.postMessage(`JSError while downloading document, url: ${url}, err: ${err}`)  
                }  
            })();  
            // null is needed here as this eval returns the last statement and we can't return a promise  
            null;  
        """) { (result, err) in  
            if (err != nil) {  
                debugPrint("JS ERR: \(String(describing: err))")  
            }  
        }  
    }  
}  

关于swift - 如何在wkwebview中下载文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59083340/

相关文章:

ios - 设置WKWebView scrollView的contentInset导致页面跳转到顶部

javascript - 我如何等待回调来解决 JS 中的 promise ?同步 iOS WKWebView 和 JS

ios - Swift:向 WKWebView 注入(inject) JS 和 CSS 不起作用

ios - 应用程序在 sendEvent 方法上崩溃

iOS-Swift,Stripe 创建临时 key 返回 Google 登录页面

Swift 泛型继承

json - 无法解码 token : The token ###

javascript - 外部 JS 是否可以在 iOS WkWebView 的加载页面(完成后)上执行

ios - WKWebView如何显示指向http : pages的链接

ios - 如何将我的操作添加到标准编辑 tableView 按钮 iOS