WKWebView采用HybridNSURLProtocol协议拦截图片等资源预加载

强调内容采用[NSURLProtocol registerClass:[HybridNSURLProtocol class]];注册协议,自定义scheme,用canInitWithRequest函数拦截指定图片资源下载地址,建立PHAsset,通过requestImageDataForAsset获取本地图片对象,新建NSURLResponse并返回图片数据流给js。
需求场景:js通过设置函数标签,加载客户端本地图片资源,达到图片预览的目的。
例如:我们的图片分两步上传,第二步就用到该应用场景达到上传时的图片预览功能。我们应用的图片上传是分两步实现了。第一步:是服务器的js通过客户端注册函数下发图片选择请求,用户选择图片后通过回调机制发送选在的本地图片地址拼装消息数组数据给服务器,注意这一步只是向服务器发送加工过的图片本地地址到服务器,并没有上传图片二进制流。
第二步:1.JS页面通过客户端注册函数下发单个加工过的本地图片地址及上传消息类型,客户端根据这个自定义URL schemes的本地图片地址获取本地图片数据并上传数据到阿里云,上传成功js继续下发相同的消息,客户端按照这个逻辑继续上传图片到服务器。
2.JS页面下上传图片消息的同时通过标签函数调用下载图片数据,客户端通过注册自定义URL schemes拦截该自定义的URL schemes图片下载请求并且返回图片数据,达到在图片上传图片过程中就在js页面显示图片的目的。注意:图片的URL schemes不能使用系统自带的https,不然会出现,post请求body参数被清空,js页面无法通过post请求传递参数的问题。具体参照:《【腾讯Bugly干货分享】WKWebView 那些坑》
由于我是做app开发的,无法在服务器上放置代码让你测试,所以可以采用HybridNSURLProtocol协议拦截图片等资源可以去github上这个地址下载这个demo。不过这个demo有一个严重问题,由于它采用是 [NSURLProtocol wk_registerScheme:@”http”];
[NSURLProtocol wk_registerScheme:@”https”];拦截,导致post请求body参数被清空,js页面无法通过post请求传递参数的问题,上面有介绍,我们在开发过程中也遇到过。所以你的页面需要通过post的方式上传参数时,一定不能按照demo那么用,要自定义URL schemes。
下面来些干货:
第一步:下载哪个demo。
第二步:其中HybridNSURLProtocol.h(.m)和NSURLProtocol+WKWebVIew.h(.m)文件加入你的工程中。
第三步:在AppDelegate.m文件导入头文件#import “HybridNSURLProtocol.h”,加入下面的代码:
- (BOOL)application:(UIApplication )application didFinishLaunchingWithOptions:(NSDictionary )launchOptions {
[NSURLProtocol registerClass:[HybridNSURLProtocol class]];

return YES;

}
第四步:在你需要拦截js标签函数WKWebView页面导入头文件#import “NSURLProtocol+WKWebVIew.h”,加入如下代码:
- (void)viewDidLoad {
[super viewDidLoad];
[self registerNSURLProtocolScheme];
}

  • (void)registerNSURLProtocolScheme
    {
    [NSURLProtocol wk_registerScheme:@”yxLocalFile”];
    [NSURLProtocol wk_registerScheme:@”yxlocalfile”];
    // [NSURLProtocol wk_registerScheme:@”https”];
    }
    第五步:HybridNSURLProtocol.m加入图片等资源拦截与取本地图片资源组装响应消息给js。我们的应用只对图片地址进行拦截并返回本地数据给js。cs和js等资源拦截可以参照这个写。代码如下:

import “HybridNSURLProtocol.h”

import “TZImagePickerController.h”

import

            TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:self.homeParamsEntity.chooseImageEntity.count delegate:self];

            // You can get the photos by block, the same as by delegate.
            // 你可以通过block或者代理,来得到用户选择的照片.
            [imagePickerVc setDidFinishPickingPhotosHandle:^(NSArray<UIImage *> *photos, NSArray *assets, BOOL isSelectOriginalPhoto) {
                @strongify(self);
                [self.wkWebWeipaiJSBridgeModel chooseImageResponseWithPhotos:photos assets:assets];
            }];
            [self presentViewController:imagePickerVc animated:YES completion:nil];

通过TZImagePickerController组件获取到选择图片的PHAsset对象数组。下面是如何通过PHAsset对象数组拼装消息给js。

-(void)chooseImageResponseWithPhotos:(NSArray<UIImage *> *)photos
                              assets:(NSArray *)assets
{

    if((photos.count == 0) || (assets.count == 0))
    {
        return;
    }
    NSMutableArray *localIds = [NSMutableArray array];
    for(NSUInteger i = 0; (i < assets.count) && (i < photos.count); i++)
    {
        PHAsset *phAsset = assets[i];
        NSString *localIdentifier = phAsset.localIdentifier;
        NSString*fileName=[phAsset valueForKey:@"filename"];
        NSLog(@"File name %@",fileName);
        NSArray* arr = [fileName componentsSeparatedByString:@"."];
        NSString *fileExtend = @"";
        if(arr.count != 0)
        {
            fileExtend = [NSString stringWithFormat:@".%@", arr[arr.count - 1]];
        }
        NSString *yxLocalFile = [NSString stringWithFormat:@"yxLocalFile://%@%@", localIdentifier,fileExtend];
        [localIds addSafeObject:yxLocalFile];
    }
    NSLog(@"localIds :%@", localIds);
    NSString *localIdsString = [localIds getJsonString];
    NSLog(@"localIdsString :%@", localIdsString);
    NSString *callBackStr = @"";
    if(isEmptyString(localIdsString))
    {
        callBackStr = @"wxCallBack.chooseImage.fail();";
    }
    else
    {
    callBackStr = [NSString stringWithFormat:@"wxCallBack.chooseImage.success({\"localIds\":%@});", localIdsString];
    }
    NSLog(@"callBackStr :%@", callBackStr);

    [self callBackWithCallBackStr:callBackStr];
}
-(void)callBackWithCallBackStr:(NSString *)callBackStr
{
    FLDDLogVerbose(@"callBackStr :%@", callBackStr);
    if(isEmptyString(callBackStr))
    {
        return;
    }
    yx_dispatch_main_async_safe(^{
        //oc调用js的shareResult方法并且传递参数
        [self.homeParamsEntity.wkWebView evaluateJavaScript:callBackStr completionHandler:^(id _Nullable data, NSError * _Nullable error) {


        }];
    });
}

下面是NSArray扩展类的函数:

-(NSString *)getJsonString
{
    NSError *error;
    NSData *infoData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
    NSString *infoString = @"";
    if (infoData) {
        infoString = [[NSString alloc] initWithData:infoData encoding:NSUTF8StringEncoding];
        infoString = [infoString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

        infoString = [infoString stringByReplacingOccurrencesOfString:@"\n" withString:@""];
        infoString = [infoString stringByReplacingOccurrencesOfString:@" " withString:@""];
    }
    return infoString;
}

猜你喜欢

转载自blog.csdn.net/jia12216/article/details/82219836