• macOS/iOS WKWebview 下载文件


    WKWebview 下载文件需要通过JS注入的方式来下载。js下载的数据是base64编码的,回调给原生后,原生需要反编码后才是原始文件的数据。
    具体步骤:

    1. 配置WKWebview的js回调句柄(标识)
    2. 创建WKWebview并添加到视图上
    3. 实现WKScriptMessageHandler的(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message,处理下载的数据
    4. 实现WKNavigationDelegate的 (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler,注入js下载代码

    配置WKWebview的js回调句柄(标识)并创建

    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    WKPreferences *preference = config.preferences;
    preference.javaScriptEnabled = YES;
    WKUserContentController * wkUController = [[WKUserContentController alloc] init];
    [wkUController addScriptMessageHandler:self name:@"onBlobData"];
    config.userContentController = wkUController;
    self.webView = [[WKWebview alloc] initWithFrame:self.bounds configuration:config];
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    关键代码 [wkUController addScriptMessageHandler:self name:@“onBlobData”]; onBlobData 就是我们定义给js调回来的接口或标识

    #实现WKScriptMessageHandler 代理
    重点实现函数didReceiveScriptMessage,这里需要捕获我们上一步定义的标识事件“onBlobData”,并处理对应的数据

    -(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
        if ([message.name isEqualToString:@"onBlobData"]) {
            NSString * content = message.body;
            content = [content stringByReplacingOccurrencesOfString:@"data:text/xml;base64," withString:@""];// 踢出头部信息
            [self saveFile:content];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    content 的值如下:

    data:text/xml;base64,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
    
    • 1

    其中前面的’data:text/xml;base64,'表示文件的数据:数据内容格式;编码方式。对于不同的文件有不同的内容格式,可根据具体已知要下载的文件类型存取或进一步判别这个头数据来处理。取数据时要去除这个头部信息之后再反编码,直接存文件即可。

    存文件

    存文件的时候我们可以存到我们指定的位置,一般情况下是弹框让用户来选定存放位置。

    - (void)saveFile:(NSString *)content {
        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
        [dateFormatter setDateFormat:@"yyyyMMddHHmmss"];
        NSString *dateString = [dateFormatter stringFromDate:[NSDate date]];
        NSString *filename = [NSString stringWithFormat:@"下载文件_%@.txt",dateString];
        NSSavePanel *panel = [NSSavePanel savePanel];
        [panel setAllowsOtherFileTypes:YES];
        [panel setAllowedFileTypes:[NSArray arrayWithObjects:@"txt", nil]];
        [panel setNameFieldStringValue:filename];
        [panel setExtensionHidden:YES];
        [panel setCanCreateDirectories:YES];
        [panel beginSheetModalForWindow:self.window completionHandler:^(NSInteger result){
            if (result == NSFileHandlingPanelOKButton){
                NSString *filePath = [[panel URL]path];
                dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
                    NSData *data =[[NSData alloc] initWithBase64EncodedString:content options:0];//base64 反编码
                    // Generate the file path
                    [data writeToFile:filePath atomically:YES];//存到指定文件(直接写入)
                });
            }
        }];
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    js下载代码注入

    实现WKNavigationDelegate代理,并在decidePolicyForNavigationAction 函数中捕获要下载的bloburl,后进行下载

    - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
        NSLog(@"decidePolicyForNavigationAction :%@",navigationAction.request);
        if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
            NSString *loadUrl = navigationAction.request.URL.absoluteString;
            if ([loadUrl containsString:@"blob:"] && [[loadUrl substringWithRange:NSMakeRange(0, 5)] isEqualToString:@"blob:"]) {
                NSString *jsFormat = [NSString stringWithFormat:@"var xhr = new XMLHttpRequest();"
                                @"xhr.open('GET', '%@', true);"
                                @"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() {"
                                        @"window.webkit.messageHandlers.onBlobData.postMessage(reader.result);" //通过onBlobData 调回给oc代码
                                       @"}"
                                     @"}"
                                 @"};"
                                @"xhr.send();",loadUrl];
                NSString * strJSCode = [NSString stringWithFormat:@"%@", jsFormat];
                [webView evaluateJavaScript:strJSCode completionHandler:^(id _Nullable data, NSError * _Nullable error) {
                    NSLog(@"blob:%@",strJSCode);
                }];
            }
            decisionHandler(WKNavigationActionPolicyCancel);
            NSLog(@"WKNavigationActionPolicyCancel");
        } else {
            decisionHandler(WKNavigationActionPolicyAllow);
            NSLog(@"WKNavigationActionPolicyAllow");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    重点说明xhr.open(‘GET’, ‘%@’, true); 第二个参数需要替换为捕获到的blob url,有些文章中介绍时使用‘(blob)’,就有人照抄代码,调用了没有任何结果回调,且还不知道是注入的代码不对还是请求不成功,文中通过stringWithFormat将loadurl格式化到js代码中,代码直接可用。

  • 相关阅读:
    面试题:Java序列化与反序列化
    C基础学习之C 函数指针
    Apache Druid连接回收引发的血案
    HTML超链接标签
    MySQL——备份和还原
    mmap()
    JSP在线客户服务支持管理系统myeclipse开发mysql数据库bs框架java编程jdbc
    业务工人业务实体元模型-软件方法(下)第9章分析类图案例篇Part09
    C语言——文件
    磷酸酶、转录因子、KRAS ——“不可成药”靶点?
  • 原文地址:https://blog.csdn.net/lanlangaogao/article/details/127905521