当前位置: 移动技术网 > IT编程>移动开发>IOS > iOS WebView中使用webp格式图片的方法

iOS WebView中使用webp格式图片的方法

2019年07月24日  | 移动技术网IT编程  | 我要评论

联谊代孕,天庭剩女,刘桃绫微博

webp格式图片

webp格式图片是google推出的,相比jpg png有着巨大的优势,同样质量的图片webp格式的图片占用空间更小,在像电商这样图片比较多的app中,使用webp格式图片会很有优势。

引言

很早之前,我们的项目中就已经采用了webp格式,但是由于webview本身并不能解析webp格式,所以我们基于webview的文章详情页就无法使用到这项优化。 

那么有没有什么办法能实现呢?当然是有的。

在开始技术讲解之前需要先说明,本文的技术方案,是基于本项目的情况:文章的正文大部分通过接口直接获取到,通过在客户端本地进行html正文组装,最后通过webview的loadhtmlstring方法进行加载显示。普通的图片可以通过转换链接得到webp服务器获取到相应的webp版的图片。

本项目中,图片缓存使用了sdwebimage,并且开启了webp支持功能,那么我们对详情页webview的处理也会基于此来实现。 

通过思考,方案其实还是比较明确的,就是替换html中图片链接,通过客户端下载webp图片,然后在通过js刷新出页面上的下完的图片,但实际开发中也遇到了一些坑,比如:

  • html解析库的setattributenamed不能增加属性
  • webp服务器图片下载后的默认缓存时gif不能正常存储
  • 下载完的图片不能实时通过js更改src为本地文件地址加载出来

最终的技术实现:

1.对下载回来的html内容进行处理,获取所有图片链接,并进行webp链接处理转换 

对html内容的解析处理我使用的是objective-c-hmtl-parser,但是该库已经多年不维护,这里有我fork后进行部分优化调整的版本:https://github.com/yueruo/objective-c-hmtl-parser ()

处理html图片核心处理逻辑代码:

@try {
 htmlparser *parser = [[htmlparser alloc] initwithstring:htmlcontent error:&error];
 htmlnode *bodynode = [parser body];
 if (error) {
 return;
 }
 //得到所有的img标签
 nsarray *inputnodes = [bodynode findchildtags:@"img"];

 for (htmlnode *inputnode in inputnodes) {
 nsstring *imagesrc = [inputnode getattributenamed:@"src"];
 if (!imagesrc) {
  continue;
 }
 nsstring *newsrc = [[globalvariable shareinstance] resizewebpimagewithurl:imagesrc size:cgsizemake((screen_width - 20) * 2, 0)];//根据原图片,得到webp服务器使用的图片链接,需要有webp处理服务器
 //检查本地图片缓存
 nsstring *key = [[sdwebimagemanager sharedmanager] cachekeyforurl:[nsurl urlwithstring:newsrc]];
 nsstring *localpath = [[sdimagecache sharedimagecache] defaultcachepathforkey:key];
 nsstring *webpimage = newsrc;
 bool localexsit = [[nsfilemanager defaultmanager] fileexistsatpath:localpath];
 if (localexsit) {
  newsrc = [nsstring stringwithformat:@"file://%@", localpath];
 }
 //存储疑似webp图片和原图片,如果newsrc和webp相同则说明本地没有缓存图片
 [_webpimageurldic setobject:webpimage forkey:newsrc];
 if(localexsit){
  setattributenamed(inputnode->_node, "src", [newsrc cstringusingencoding:nsutf8stringencoding]);
 }else{
  setattributenamed(inputnode->_node, "src", "详情页占位图@2x.png");
 }
 //给img标签中增加一个叫osrc的属性,便于后续处理
 setattributenamed(inputnode->_node, "osrc", [newsrc cstringusingencoding:nsutf8stringencoding]);
 }
 htmlcontent = [nsmutablestring stringwithstring:parser.doc.rawcontents];
}
@catch (nsexception *exception) {
}
@finally {
 [webview loadhtmlstring:htmlcontent baseurl:baseurl];
}

2.用原生方法下载webp图片,缓存到本地 

下载之后会存储为jpg或png格式,这样就可以被webview进行本地加载,但是需要注意gif的存储特殊处理。 

另外通过实验,直接通过js无法实时更新下载到本地的图片,只好通过图片的base64encode数据加载方式实现。 

具体代码如下:

- (void)webviewdidfinishload:(uiwebview *)web {
 //处理webp格式加载
 [_webpimageurldic enumeratekeysandobjectsusingblock:^(id _nonnull key, id _nonnull obj, bool * _nonnull stop) {
 if([obj isequaltostring:key]){//说明这图没有缓存,还需要下载
  [[sdwebimagedownloader shareddownloader] downloadimagewithurl:[nsurl urlwithstring:obj] options:0 progress:nil completed:^(uiimage *image, nsdata *data, nserror *error, bool finished) {
  if (image&&finished) {
   nsstring *js;
   nsrange range = [[obj lowercasestring] rangeofstring:@".gif"];//检查是否是gif
   bool isgif = (range.location != nsnotfound);
   if (!isgif) {
   [[sdimagecache sharedimagecache] storeimage:image forkey:obj];
   nsstring *base64 = [uiimagejpegrepresentation(image,1) base64encodedstringwithoptions:0];
   js = [nsstring stringwithformat:@"replacewebpimg('%@','data:image/jpeg;base64,%@')",key,base64];
   }else{//gif的图片如果直接存储,会变成jpg从而失去动画,因此要特殊处理
   [[sdimagecache sharedimagecache] storeimage:image recalculatefromimage:false imagedata:data forkey:key todisk:true];
   nsstring *base64 = [data base64encodedstringwithoptions:0];
   js = [nsstring stringwithformat:@"replacewebpimg('%@','data:image/gif;base64,%@')",key,base64];
   }
   [nsthread excuteinmainthread:^{
   [webview stringbyevaluatingjavascriptfromstring:js];
   } async:false];
  }
  }];
 } else {//缓存中存在,那么直接加载吧
  nsstring *js;
  nsrange range = [[obj lowercasestring] rangeofstring:@".gif"];//检查是否是gif
  nsdata* data = [nsdata datawithcontentsoffile:[key stringbyreplacingoccurrencesofstring:@"file://" withstring:@""]];
  nsstring *base64 = [data base64encodedstringwithoptions:0];
  bool isgif = (range.location != nsnotfound);
  if (!isgif) {
  js = [nsstring stringwithformat:@"replacewebpimg('%@','data:image/jpeg;base64,%@')",obj,base64];
  }else{
  js = [nsstring stringwithformat:@"replacewebpimg('%@','data:image/gif;base64,%@')",obj,base64];
  }
  [nsthread excuteinmainthread:^{
  [webview stringbyevaluatingjavascriptfromstring:js];
  } async:false];
 }
 }];
} 

3.回调webview页面,用本地链接替换原有的图片 

加载已下载好的图片,这里主要通过js来实现,即第2步中的replacewebpimg方法,该js方法可通过提前置于html的模板中,或者webviewdidfinishload后采用js注入进去

replacewebpimg = function(src, localpath) {
 var imgs = document.queryselectorall('img[osrc="'+src+'"]'),len = imgs.length;;
 for (var i = 0; i < len; i++) {
 var img = imgs[i];
 img.src = localpath;
 }
}

好再次总结一下整个流程:

  • 对服务器返回的htmlcontent数据进行相应处理,检查图片是否存在缓存,存在则使用本地地址为src,不存在则把图片的src替换成占位图。记录下图片地址,并增加属性做好标记。
  • 图片的地址进行webp转换,通过客户端进行下载
  • 下载后的图片,通过js方法进行src更改,并且赋值的base64的图片编码数据,因为给本地地址无法实时展示出来

这篇写的比较简单,更详细的步骤请查阅上面的代码,里面我加上还算详细的注释,希望对想要在webview中使用webp图片的大家有所帮助。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网