How to download files in wkwebview

萝らか妹 提交于 2021-02-19 03:49:31

问题


Anybody please tell me how to download files in iOS wkwebview. I have created an iOS webview app. In the page I have loaded It has several download options but when I click download nothing happened.

Note: I dont want to create extra buttons to download


回答1:


You can also use JavaScript to download your file, as Sayooj's link implys.

Of cource, you will handle the file downloaded code yourself.

With func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {, you get the file url to download.

Then download it with JS.

the JS call a downloaded method if success, you will be notified with public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {,

Then you can handle your downloaded file

It is a little complicated. Use JavaScript to download file, use WKScriptMessageHandler to communicate between native Swift and 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))")  
            }  
        }  
    }  
}  



回答2:


As Sayooj's link implys:

You have to deal with the download business yourself

After you have download task in WKWebView, you can get the file url to download from method func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {

Then you initiate a download task to download the file, URLSession is an option

you can handle the file after downloaded. And the link above shows how to preview your downloaded file with QLPreviewController

import UIKit    
import WebKit      

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {    

    var webView: WKWebView!   

    var webViewCookieStore: WKHTTPCookieStore!  
    let webViewConfiguration = WKWebViewConfiguration()  

    override func viewDidLoad() {  
        super.viewDidLoad()  

        webView = WKWebView(frame: yourFrame, configuration: webViewConfiguration)   
        webView.uiDelegate = self  
        webView.navigationDelegate = self  

        view.addSubview(webView)  
        webView.load(URLRequest(url: yourUrlString))    
    }    


    /*  
     Needs to be intercepted here, because I need the suggestedFilename for download  
     */    
    func webView(_ webView: WKWebView,    
                 decidePolicyFor navigationResponse: WKNavigationResponse,    
                 decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {    
        let url = navigationResponse.response.url    

        let documentUrl = url?.appendingPathComponent(navigationResponse.response.suggestedFilename!)    
        loadAndDisplayDocumentFrom(url: documentUrl!)    
        decisionHandler(.cancel)    

    }    

    /*  
     Download the file from the given url and store it locally in the app's temp folder.   
     */    
    private func loadAndDisplayDocumentFrom(url downloadUrl : URL) {  
        let localFileURL = FileManager.default.temporaryDirectory.appendingPathComponent(downloadUrl.lastPathComponent)  

            URLSession.shared.dataTask(with: downloadUrl) { data, response, err in  
                guard let data = data, err == nil else {  
                    debugPrint("Error while downloading document from url=\(downloadUrl.absoluteString): \(err.debugDescription)")  
                    return  
                }  

                if let httpResponse = response as? HTTPURLResponse {  
                    debugPrint("Download http status=\(httpResponse.statusCode)")  
                }  

                // write the downloaded data to a temporary folder  
                do {  
                    try data.write(to: localFileURL, options: .atomic)   // atomic option overwrites it if needed  
                    debugPrint("Stored document from url=\(downloadUrl.absoluteString) in folder=\(localFileURL.absoluteString)")  

                    DispatchQueue.main.async {  
                        // localFileURL  
                        // here is where your file 

                    }  
                } catch {  
                    debugPrint(error)  
                    return  
                }  
            }.resume()  
        }  
    }  


来源:https://stackoverflow.com/questions/59083340/how-to-download-files-in-wkwebview

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!