如何拦截 WKWebView 请求以检测哪些本地资源文件(css、js、png 等)与 HTML 文件一起加载?

Ton*_*ham 7 javascript css nsurlprotocol wkwebview wkurlschemehandler

我有一个 HTML 文件,其中包含本地资源文件,例如其内容中的 css、js 和 png 文件。这些本地资源文件采用 zip 格式。我的应用程序使用 WKWebView 来显示这个 html 文件。我想找到一个解决方案来拦截 web 视图请求,以检测哪些本地资源文件与此 html 文件一起加载 -> 如果它们仍然是 zip 格式,则将它们解压缩。

我的 HTML 数据内容包含数千个这样的本地资源文件,因此在显示内容之前我无法解压缩所有这些文件。使用 UIWebView,我们使用 NSURLProtocol 子类拦截请求,检测本地资源文件并根据用户正在查看的 html 页面按需解压缩。

将 UIWebView 转换为 WKWebView 时遇到此问题。类似的问题在这里发布:https : //forums.developer.apple.com/thread/87474

======== 更新 ========>

我通过使用WKURLSchemeHandler 弄清楚了

注意:您需要将文件方案更改为自定义方案才能使用 WKURLSchemeHandler,因为它不适用于文件、http、https 等标准方案。

1. 向 WKWebView 注册自定义方案

    let configuration = WKWebViewConfiguration()
    configuration.setURLSchemeHandler(self, forURLScheme: "x-file")
    webView = WKWebView(frame: view.bounds, configuration: configuration)
Run Code Online (Sandbox Code Playgroud)

2. 将文件方案转换为自定义方案(x-file),然后使用 WKWebView 加载它

    let htmlPath = Bundle.main.path(forResource: "index", ofType: "html")
    var htmlURL = URL(fileURLWithPath: htmlPath!, isDirectory: false)                    
    htmlURL = self.changeURLScheme(newScheme: "x-file", forURL: htmlURL)
    self.webView.load(URLRequest(url: htmlURL))
Run Code Online (Sandbox Code Playgroud)


3、实现WKURLSchemeHandler协议的2个方法,处理WKURLSchemeTask的3个委托方法。

    func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
        print("Function: \(#function), line: \(#line)")
        print("==> \(urlSchemeTask.request.url?.absoluteString ?? "")\n")

        // Your local resource files will be catch here. You can determine it by checking the urlSchemeTask.request.url.
        // From here I will unzip local resource files (js, css, png,...) if they are still in zip format
        ....

        // Handle WKURLSchemeTask delegate methods
        let url = changeURLScheme(newScheme: "file", forURL: urlSchemeTask.request.url!)

        do {
            let data = try Data(contentsOf: url)

            urlSchemeTask.didReceive(URLResponse(url: urlSchemeTask.request.url!, mimeType: "text/html", expectedContentLength: data.count, textEncodingName: nil))
            urlSchemeTask.didReceive(data)
            urlSchemeTask.didFinish()
        } catch {
            print("Unexpected error when get data from URL: \(url)")
        }
    }

    func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {
        print("Function: \(#function), line: \(#line)")
        print("==> \(urlSchemeTask.request.url?.absoluteString ?? "")\n")
    }
Run Code Online (Sandbox Code Playgroud)

h3d*_*ndi 5

如果 WKWebView 加载本地 html 文件,您可以像这样授予 WKWebView 访问本地应用程序资源的权限:

NSURL *documentDirectoryURL = [NSURL fileURLWithPath:DOCUMENTS_DIRECTORY];

// This code gives acces to a file that is outside of our webview html file root directory
[self.webView loadFileURL:documentDirectoryURL allowingReadAccessToURL:documentDirectoryURL];

// If you use one of these loads after, they will too have access if they are in app html files
[self.webView loadRequest:<inAppHTMLFile>];
[self.webView loadHTMLString: baseURL:];
Run Code Online (Sandbox Code Playgroud)

这在这里可以访问文档目录中的所有文件。

当我想将本地资源链接到在线页面时,我发现您的解决方案很有用。

WKUserContentController *contentController = [[WKUserContentController alloc]init];
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
CustomFileShemeHandler *schemeHandler = [[CustomFileShemeHandler alloc] init];
[config setURLSchemeHandler:schemeHandler forURLScheme:@"myApp-images"];
config.userContentController = contentController;
self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:config];



//CustomFileShemeHandler.m

@implementation CustomFileShemeHandler

- (void)webView:(nonnull WKWebView *)webView startURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
    NSURL *customFileURL = urlSchemeTask.request.URL;
    NSString *securityFilter = [NSString stringWithFormat:@"Documents/LocalAppImages"];

    if (![customFileURL.absoluteString containsString:securityFilter]) {
        return;
    }

    NSURL *fileURL = [self changeURLScheme:customFileURL toScheme:@"file"];
    NSURLRequest* fileUrlRequest = [[NSURLRequest alloc] initWithURL:fileURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:.1];

    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:fileUrlRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
        NSURLResponse *response2 = [[NSURLResponse alloc] initWithURL:urlSchemeTask.request.URL MIMEType:response.MIMEType expectedContentLength:data.length textEncodingName:nil];
        if(error){
            [urlSchemeTask didFailWithError:error];
        }
        [urlSchemeTask didReceiveResponse:response2];
        [urlSchemeTask didReceiveData:data];
        [urlSchemeTask didFinish];
    }];

    [dataTask resume];
}

- (void)webView:(nonnull WKWebView *)webView stopURLSchemeTask:(nonnull id<WKURLSchemeTask>)urlSchemeTask {
    NSLog(@"DUNNO WHAT TO DO HERE");
}

- (NSURL *)changeURLScheme:(NSURL *)url toScheme:(NSString *)newScheme {
    NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
    components.scheme = newScheme;
    return components.URL;
}
Run Code Online (Sandbox Code Playgroud)

有了这个,在 webView 中打开的任何页面都可以使用其完整路径和自定义方案链接到本地​​资源:

<img src="myApp-images:///<YourDocumentsAppPahtHere>/LocalAppImages/funnyDog.jpg">
Run Code Online (Sandbox Code Playgroud)


kst*_*nou 5

根据h3dkandi的回答,此评论帮助我解决了我的问题:(迅速更新了代码)

如果您首先使用:

 // use this in order to access images, css files etc..
 webView.loadFileURL(baseURL, allowingReadAccessTo: baseURL) 

 // use this method to actually load the html with images, css etc..
 webView.loadHTMLString(htmlString, baseURL: baseURL)
Run Code Online (Sandbox Code Playgroud)

其中baseURL是我本地.html文件存在的路径。