web图片下载转为native下载

早期版本的网易新闻每次点开新闻图片,都会重新去下载新的图片,而在某个版本开始,可以发现点开的时候并不会重新加载了,只有在保存的时候会下载高清图片,那么他是怎么做到的呢。

平时,我们网页基本都是自己负责自己的内容加载。但是有时候却希望能够拿到网页内的图片资源,此时我们不得不自己再去加载一遍,不仅仅浪费流量,而且影响用户体验。

由客户端帮助web下载图片,这样我们就可以让web和本地共用缓存,并且可以自定义缓存了。那么如何做到这样的效果呢?这里提供两种不同的思路来解决这种问题,分别对UIWebViewWKWebView下的运行情况进行分析。Github

URLProtocol

我们知道URLProtocol可以截获请求,那么我们是否可以直接截获所有URL,然后根据path来判断是否是图片,是否需要自定义下载。

根据demo试验来看,UIWebView可以完美的实现该方案,但是WKWebView却不能被截获,原因是WKWebView拥有自己的URLSession,虽然可以用黑科技获取URLSession并加入代理,但是不能通过苹果审核。

FileURL

另一种方式是使用本地文件,这种方式更加符合规范而且更加安全。不过WKWebView支持本地资源的接口是在iOS 9.0才引入的,而且需要指定目录,所以需要先把所有资源拷贝到该目录下。

根据demo的试验来看,UIWebView和WKWebView都完美的支持了。

比较

现在来比较下这几种方案。

  1. URLProtocol能够在不改变网页内容的情况下直接对内容进行替换,但是不能支持WKWebView,而且全局的Protocol可能会带来某些隐患。
  2. FileURL能够灵活的定制化下载方式,而且能够支持WKWebView,但是需要将img标签的http链接替换为file链接。

适用场景

URLProtocol比较通用,普遍适用于各种网页。

FileURL更加偏向于定制化,这里来详细看看定制化的场景。

比如网易新闻的新闻内容格式大概是这样的:

<p>正文正文正文</p>
![](http://upload-images.jianshu.io/upload_images/1929223-20452a7eea9eca88.png)

很多情况下,为了优化网络以及渲染,都需要类似的简化html,并且把css,js代码打包到App或者单独下载。那么其实我们有很多情况下是拥有一定固定格式的html代码段。而部分活动等特殊的网页我们一般也不会需要客户端下载资源来优化性能。那么有了这个前提,FileURL的适用面其实还是很广的。

动态创建DOM

那么怎么样处理比较合适呢?我们以WKWebView来看,难道我们要首先用拼装好一个网页,然后保存为一个文件进行载入吗?

当然不需要这么笨重的解决方案,这样做不仅影响了性能,而且特别不灵活,一旦内容需要变更就会特别麻烦。

目前前端技术有两个热门的概念,虚拟DOM和响应式,那么我们也可以利用类似的原理。下面是一个简单的实现。

// 在Web端:
var imageList = []

// 创建一个空的节点,用来占位和展示进度条
function appendImage(url) {
    let dom = document.createElement('div')
    dom.style.padding = '20px 10px'

    let img = document.createElement('img')

    let p = document.createElement('p')
    p.innerText = '0%'

    dom.appendChild(p)
    dom.appendChild(img)

    document.body.appendChild(dom)
    imageList.push({url, dom})
}

function findDomByUrl(url) {
    let obj = imageList.find(i => i.url == url)
    return obj && obj.dom
}

function updateText(url, text) {
    // console.log(`update text at <${url}> with "${text}"`)
    let dom = findDomByUrl(url)
    if (dom) {
        dom.firstChild.innerText = text
    }
}

// 更新进度条
function updateProgress(url, progress) {
    updateText(url, progress)
}

// 更新错误状态
function updateError(url, error) {
    updateText(url, error);
}

// 图片下载完成后,更新图片资源
function updateImage(url, fileUrl) {
    // console.log(`update image url at <${url}> with "${fileUrl}"`)
    let dom = findDomByUrl(url)
    if (dom) {
        dom.lastChild.src = fileUrl
    }
}
// 客户端
// 初始化
[self.WKWebView evaluateJavaScript:[NSString stringWithFormat:@"appendImage('%@')", url] completionHandler:^(id _Nullable ret, NSError * _Nullable error) {
}];

// 这里为了测试伪造一个进度功能
__block NSInteger progress = 0;
if ([url hasPrefix:@"http"]) [NSTimer scheduledTimerWithTimeInterval:0.1 repeats:YES block:^(NSTimer * timer) {
    progress += 5;
    [self.WKWebView evaluateJavaScript:[NSString stringWithFormat:@"updateProgress('%@', '%zd')", url, progress] completionHandler:^(id _Nullable ret, NSError * _Nullable error) {

    }];
    if (progress >= 100) {
        [timer invalidate];
    }
}];
// 下载图片
[[[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:url] completionHandler:^(NSData * data, NSURLResponse * response, NSError * error) {
    if (error) {
        // 错误态
        [self.WKWebView evaluateJavaScript:[NSString stringWithFormat:@"updateError('%@', '%@')", url, error.localizedDescription] completionHandler:^(id _Nullable ret, NSError * _Nullable error) {

        }];
    }
    else {
        // 保存图片并且更新界面
        NSURL * fileUrl = [self.cacheUrl URLByAppendingPathComponent:url.lastPathComponent];
        [data writeToURL:fileUrl atomically:YES];
        [self.WKWebView evaluateJavaScript:[NSString stringWithFormat:@"updateImage('%@', '%@')", url, fileUrl.absoluteString] completionHandler:^(id ret, NSError * error) {

        }];
    }
}] resume];

这样我们就可以动态的更新界面了,而且我们拥有自己创建的dom信息,那么我们可以做更多的事情:)

最后

这里讨论了两种把图片转为客户端下载的方式,如果要说如果这么做相当于客户端来编写网页了,还不如直接写原生的呢。但是这样做比原生更加灵活,可以应用不同模板,不同样式,就可以改变外观了。而且具体使用时也不一定需要这么死板的去创建dom,之后我会尝试下不同的方案来简化双方的FileURL实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容

  • 通过学习,你将会学习以下几个方面的内容: **什么是WKWebView以及它和UIWebView的区别是什么 **...
    SOI阅读 11,621评论 18 42
  • 前言 关于UIWebView的介绍,相信看过上文的小伙伴们,已经大概清楚了吧,如果有问题,欢迎提问。 本文是本系列...
    CoderLF阅读 8,955评论 2 12
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,949评论 25 707
  • 好像一生中重要的考试全聚集在前半生,除了高考作为一次失败的体验,其实别的考试还不错,比如考研,比如找工作。 那时刚...
    蕊蕊啊阅读 290评论 0 0
  • 公司组织志愿活动,新华小区打扫卫生,午餐统一定制外卖套餐:盖浇饭外加每人一盒稀饭! 土豆香肠木耳,洋葱大肠,白米饭...
    枯藤残鸦阅读 267评论 3 1