在开发过程中很多人都会使用SDWebImage来进行网络图片的缓存,说实话,这个第三方也确实好用,但依照本人的性格,还是一直在想做一版自己的本地缓存,比如下载的图片存到本地,下次再有相同的图片需要加载,就不用再花冤枉流量来下载了,这次的尝试虽然相比SDWebImage会差很远,但是这只是一个开始,毕竟本人的项目经验有限,但会在以后的学习中不断的对这个代码进行优化,毕竟自己的代码可控性要比第三方大得多。

最新代码的GitHub:https://github.com/YRunIntoLove/YWebImage

基本的思路就是:第一次加载图片是要下载的,在下载完成后,存到沙盒目录一个固定的文件夹(YWebImageFile)下,下次再有相同的url图片的时候,首先会从这个文件夹中进行查找,如果存在,从文件中取出,如果不存在,那就下载,待下载完毕后存到该文件夹下,因为这个类是想用在公司的项目中,用的语言就不再是Swfit

从网上查找高清大图,因为这样子才会观察得到一个过程,百度就好,大家都懂得,这里是楼主测试用的图片地址,下面测试的url是http://c.hiphotos.baidu.com/zhidao/pic/item/730e0cf3d7ca7bcb48f80cb9bc096b63f724a8a1.jpg,测试url的第三个。

static NSString * testImageURL1 = @"http://www.bz55.com/uploads/allimg/150417/139-15041G02614.jpg";
static NSString * testImageURL2 = @"http://a.hiphotos.baidu.com/zhidao/pic/item/faedab64034f78f0b7111ba67b310a55b3191c48.jpg";
static NSString * testImageURL3 = @"http://c.hiphotos.baidu.com/zhidao/pic/item/730e0cf3d7ca7bcb48f80cb9bc096b63f724a8a1.jpg";

因为要看到一个过程,所以最好有一个手动的开始点,所以加载图片的代码就写在了一个按钮的点击事件里面了,这样最好控嘛

- (IBAction)startLoadImage:(id)sender
{//默认初始化label为本地self.label.text = @"本地图片!";NSString * imageURL = self.inputView.text;//可看进度的方法[self.imageView yw_setImageWithUrl:imageURL withProgressHandle:^(CGFloat didFinish, CGFloat didFinishTotal, CGFloat Total) {//更改LabelNSString * progress = [NSString stringWithFormat:@"%.1f%%",(didFinishTotal * 1.0 / Total) * 100.0];self.label.text = progress;}];
}

加载效果图:

不知道为啥,今天的网速格外的好,从打印的效果来看表示本地已经存在了,不信还可以看看此文件下已经有了缓存文件,如果加载出现了App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.的打印提示,可以参考之前的一篇博客App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure 解决

文件中的缓存:

效果图完毕,来说说原理吧,其实原理前面也说了,就是首先根据url来判断本地是否已经存在UIImage对象,如果存在,直接取出,如果不存在,那么就需要下载,然后再将UIImage取出,设置给UIImageView即可,说到文件的操作,用到的一个系统单例是:NSFileManager,以及网络下载NSURLSession,不得不说楼主是第一次接触这两个类,惭愧,但这两个类的功能也是非常的强,特别是NSURLSession,是用来替代之前的NSURLConnection的,功能很爽,楼主最近也一直在接触这个类。不说这个了,上代码来瞅瞅吧,楼主的习惯就是分成几个类,所以按照类来说明吧。

YWebFileManager

这个类的功能就是:如果本地有这个图片文件,从本地中获取图片,是为了在后面的类目中进行调用的,外界用的时候不需要调用的,在以后的维护中会陆续添加一些新的功能,一下是这个类的声明文件:

//
//  YWebFileManager.h
//  WebImageDemo
//
//  Created by YueWen on 16/3/20.
//  Copyright © 2016年 YueWen. All rights reserved.
//#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>/***  负责处理本地文件(沙盒存储)的管理者*/
NS_CLASS_AVAILABLE_IOS(7_0) @interface YWebFileManager : NSObject/***  默认存储文件夹的大小,单位为MB*/
//@property (nonatomic, copy, readonly)NSString * fileSize;/***  单例方法**  @return YWebFileManager单例对象*/
+ (instancetype)shareInstanceType NS_AVAILABLE_IOS(7_0);/***  在沙盒中目录中默认存储的文件夹中是否存在该文件**  @param url 图片存在的url**  @return true表示存在,false表示不存在*/
- (BOOL)fileIsExist:(NSString *)url NS_AVAILABLE_IOS(7_0);/***  根据url获取存在本地的图片**  @param url 下载的url**  @return 存在的返回UIImage,不存在返回nil*/
- (UIImage *)imageWithURL:(NSString *)url NS_AVAILABLE_IOS(7_0);@end

实现方法:

这里面有两个属性,重写一下这两个属性的getter方法方便取值,一个是获取系统单例NSFileManager,另一个就是获取沙盒路径

#pragma mark - Getter-(NSFileManager *)fileManager
{return [NSFileManager defaultManager];
}-(NSString *)documentPath
{return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) firstObject];
}

不管是iOS还是其他的操作,在对文件操作的时候,肯定会检测文件存在不存在的问题,对此也实现了两个方法,一个是检测是否存在相应的文件夹,一个是检测是否存在文件,后者就是用来取出图片对象的关键:

//文件夹是否在沙盒存在
- (BOOL)folderIsExist:(NSString *)folderPath
{return [self.fileManager fileExistsAtPath:folderPath];
}//沙盒目录中默认存储文件夹中是否存在这个文件
- (BOOL)fileIsExist:(NSString *)url
{//拼接路径NSString * path = [self.documentPath stringByAppendingFormat:@"/%@/%@",defaultFolderName,url];return [self.fileManager fileExistsAtPath:path];
}

最后一个便是获取图片的方法,UIImageView + YWebImage中就是通过这个接口来获取响应的图片对象的

//根据保存的路径获取图片对象
-(UIImage *)imageWithURL:(NSString *)url
{//不存在图片返回nilif (![self fileIsExist:url]){return nil;}//拼接路径NSString * path = [self.documentPath stringByAppendingFormat:@"/%@/%@",defaultFolderName,url];//存在图片返回图片return [UIImage  imageWithContentsOfFile:path];
}

YWebDownManager

顾名思义,当然这个类就是负责下载图片的类了,里面实现了NSURLSession类进行了封装,当然,这里面用到的只是NSURLSession功能的九牛一毛,对外接口如下:
//
//  YWebDownManager.h
//  WebImageDemo
//
//  Created by YueWen on 16/3/20.
//  Copyright © 2016年 YueWen. All rights reserved.
//#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>/***  下载成功后的回调**  @param path  下载成功在本地的路径*/
typedef void(^DownManagerFinishBlock)(NSString * path);/***  下载过程中的回调**  @param didFinish      本次下载的文件大小*  @param didFinishTotal 至此一共下载文件的大小*  @param Total          一共需要下载文件的大小*/
typedef void(^DownManagerProgressBlock)(CGFloat didFinish,CGFloat didFinishTotal,CGFloat Total);NS_CLASS_AVAILABLE_IOS(7_0) @interface YWebDownManager : NSObject//开始下载图片
- (void)startDownImagePath:(NSString *)imagePath NS_AVAILABLE_IOS(7_0);
- (void)startDownImageURL:(NSURL *)imageURL NS_AVAILABLE_IOS(7_0);//设置相关回调
- (void)downManagerFinishBlockHandle:(DownManagerFinishBlock)downManagerFinishBlockHandle;
- (void)downManagerProgressBlockHandle:(DownManagerProgressBlock)downManagerProgressBlockHandle;@end

实现方法:

需要了解一下各个属性的意思,至于NSString什么的为什么用copy,不用strong,也欢迎去楼主之前的博客:iOS开发-------属性用copy、strong修饰的区别了解一下
@interface YWebDownManager ()<NSURLSessionDownloadDelegate>@property (nonatomic, copy)NSString * imagePath;        //记录图片url的字符串path
@property (nonatomic, copy)NSURL * imageURL;            //请求图片的url
@property (nonatomic, copy)NSString * imageName;        //转型后的图片名称
@property (nonatomic, copy)NSString * documentPath;     //沙盒路径@property (nonatomic, copy)DownManagerFinishBlock finishBlockHandle;        //下载完成后的回调
@property (nonatomic, copy)DownManagerProgressBlock progressBlockHandle;    //下载过程中的回调@end

设置回调的方法就是一个简单赋值的过程,不需要多说了

#pragma mark - 设置回调的方法
-(void)downManagerFinishBlockHandle:(DownManagerFinishBlock)downManagerFinishBlockHandle
{self.finishBlockHandle = downManagerFinishBlockHandle;
}-(void)downManagerProgressBlockHandle:(DownManagerProgressBlock)downManagerProgressBlockHandle
{self.progressBlockHandle = downManagerProgressBlockHandle;
}

实现两个getter方法,方便取值,一个依旧是获得沙盒路径,另一个就是获得转型base64字符串的图片名称,在本地存储的图片名就是这个字符串,为什么需要转换呢,我们很多时候看到缓存的图片是不是名字特别长呢,是因为害怕图片名字上会有难识别的特殊字符,所以需要base64来转换一下。

#pragma mark - Document Path
- (NSString *)documentPath
{return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true) firstObject];
}#pragma mark - Image Name Base64
-(NSString *)imageName
{return [YWebDataHandle imageNameForBase64Handle:_imageURL.absoluteString];
}

接着开始用NSURLSession进行下载:这里会和NSURLConnection相似,因为习惯用AFNetworking,但不得不说这个第三方对于网络请求,着实强大,并且2.0版本也已经对之前的代码进行了重构,用NSURLSession代替了之前的NSURLConnection,但是楼主还是推荐看一下原生的类,拓展一下视野。

#pragma mark - 开始下载图片的方法
-(void)startDownImagePath:(NSString *)imagePath
{NSLog(@"开始下载图片啦,路径为:%@",imagePath);//赋值_imagePath = imagePath;//创建url对象NSURL * downURL = [[NSURL alloc]initWithString:_imagePath];//开始根据URL请求图片[self startDownImageURL:downURL];}- (void)startDownImageURL:(NSURL *)imageURL
{//开始赋值_imageURL = imageURL;//创建请求对象NSURLRequest * request = [NSURLRequest requestWithURL:_imageURL];//创建网络请求对象NSURLSession * session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];//获取下载对象NSURLSessionDownloadTask * downLoadTask = [session downloadTaskWithRequest:request];//开始请求[downLoadTask resume];
}

实现代理方法,主要就是往本地存以及往外传值得一个过程:

#pragma mark - NSURLSessionDownload Delegate//下载完成
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{//路径字符串NSString * path = [NSString stringWithFormat:@"%@/YWebImageFile/%@",self.documentPath,self.imageName];//获取创建下载到的路径urlNSURL * url = [NSURL fileURLWithPath:path];//获取文件管理者NSFileManager * fileManager = [NSFileManager defaultManager];//存到文件[fileManager moveItemAtURL:location toURL:url error:nil];//主线程回调dispatch_async(dispatch_get_main_queue(), ^{//执行回调,传出路径if (self.finishBlockHandle) {self.finishBlockHandle(path);}});
}- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTaskdidWriteData:(int64_t)bytesWrittentotalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{//主线程进行回调dispatch_async(dispatch_get_main_queue(), ^{//执行过程的回调if (self.progressBlockHandle) {self.progressBlockHandle(bytesWritten,totalBytesWritten,totalBytesExpectedToWrite);}});
}

YWebDataHandle

这个类就是处理字符串,将其转成base64字符,只有这么一个方法:
NS_AVAILABLE_IOS(7_0) @interface YWebDataHandle : NSObject/***  将路径或者url转成base64处理的字符串**  @param path 需要处理的字符串**  @return 处理完毕的字符串*/
+ (NSString *)imageNameForBase64Handle:(NSString *)path;@end@implementation YWebDataHandle+(NSString *)imageNameForBase64Handle:(NSString *)path
{NSData * data = [path dataUsingEncoding:NSUTF8StringEncoding];NSString * imageNameBase = [data base64EncodedStringWithOptions:0];return [imageNameBase substringToIndex:imageNameBase.length - 2];
}@end

UIImageView + YWebImage

实现UIImageView的类目,完成对图片的下载或者调用,是主要的对外接口
#import <UIKit/UIKit.h>/***  下载过程中的回调**  @param didFinish      本次下载的文件大小*  @param didFinishTotal 至此一共下载文件的大小*  @param Total          一共需要下载文件的大小*/
typedef void(^DownManagerProgressBlock)(CGFloat didFinish,CGFloat didFinishTotal,CGFloat Total);@interface UIImageView (YWebImage)//根据url设置图片
- (void)yw_setImageWithUrl:(NSString *)url;- (void)yw_setImageWithUrl:(NSString *)urlwithProgressHandle:(DownManagerProgressBlock)progresshandle;//根据url设置图片,并支持默认占位图
- (void)yw_setImageWithUrl:(NSString *)urlplaceHolderImage:(UIImage *)placeHodlerImage;- (void)yw_setImageWithUrl:(NSString *)urlplaceHolderImage:(UIImage *)placeHodlerImagewithProgressHandle:(DownManagerProgressBlock)progresshandle;@end

实现方法完成四个即可,看似是四个,有经验的人一看也就知道其实只实现一个即可,其他的调用主方法即可:

-(void)yw_setImageWithUrl:(NSString *)urlwithProgressHandle:(DownManagerProgressBlock)progresshandle
{//处理urlNSString * urlHandle = [YWebDataHandle imageNameForBase64Handle:url];//本地查询if([[YWebFileManager shareInstanceType] fileIsExist:urlHandle])//如果本地存在返回图片{NSLog(@"本地已经存在这个图片了!");self.image = [[YWebFileManager shareInstanceType] imageWithURL:urlHandle];}//不存在需要根据url下载else{NSLog(@"本地没有这个图片!");//开始下载[self downImage:url withProgressHandle:progresshandle];}
}

其它的三调用此方法即可:

{[self yw_setImageWithUrl:url withProgressHandle:^(CGFloat didFinish, CGFloat didFinishTotal, CGFloat Total) {}];
}- (void)yw_setImageWithUrl:(NSString *)url placeHolderImage:(UIImage *)placeHodlerImage
{[self yw_setImageWithUrl:url placeHolderImage:placeHodlerImage withProgressHandle:^(CGFloat didFinish, CGFloat didFinishTotal, CGFloat Total) {}];
}- (void)yw_setImageWithUrl:(NSString *)url placeHolderImage:(UIImage *)placeHodlerImage withProgressHandle:(DownManagerProgressBlock)progresshandle
{//设置占位图self.image = placeHodlerImage;//开始设置图片[self yw_setImageWithUrl:url withProgressHandle:progresshandle];
}

因为下载过程代码比较多,依照楼主的习惯还是拿出来当成一个方法:

//开始下载图片
- (void)downImage:(NSString *)url
{[self downImage:url withProgressHandle:^(CGFloat didFinish, CGFloat didFinishTotal, CGFloat Total) {}];
}//开始下载图片
- (void)downImage:(NSString *)url
withProgressHandle:(DownManagerProgressBlock)progresshandle
{YWebDownManager * webDownManager = [[YWebDownManager alloc]init];//开始下载[webDownManager startDownImagePath:url];//设置下载完毕的回调[webDownManager downManagerFinishBlockHandle:^(NSString *path) {//获得当前的图片对象UIImage * image = [UIImage imageWithContentsOfFile:path];self.image = image;}];//设置下载过程的回调[webDownManager downManagerProgressBlockHandle:^(CGFloat didFinish, CGFloat didFinishTotal, CGFloat Total) {//进行回调progresshandle(didFinish,didFinishTotal,Total);}];
}
这样基本的功能就已经实现了,以后会陆续的增加这个类的功能,[[Thanks alloc]init](对Objective-C致敬,也感谢您的观看)

ios 图片下载并保存到本地相关推荐

  1. 微信小程序下载网络图片保存到本地

    微信小程序下载网络图片保存到本地 问题背景 前一篇文章介绍了,微信小程序网络请求数据并在页面列表显示(参考 https://blog.51cto.com/baorant24/6189453 ),本文将 ...

  2. 短视频平台开发,将图片、视频保存到本地的相册中

    短视频平台开发,将图片.视频保存到本地的相册中实现的相关代码 获取本地相册 - (IBAction)goodsButton1Touch:(id)sender {//拿到获取相册的权限if([UIIma ...

  3. html2canvas将html代码生成canvas转换成图片,并且保存到本地

    html2canvas将html代码生成canvas转换成图片,并且保存到本地 html2canvas 将html转换成canvas展示 convertCanvasToImage 从canvas提取为 ...

  4. unity 下载图片使用并保存在本地

    using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; usin ...

  5. js实现html页面转图片、并保存到本地

    js实现html页面转图片,并保存图片在本地,实现原理为(html转canvas.canvas转image) 需要的库:canvas2image.js 下载地址:https://github.com/ ...

  6. js生成html转换成图片保存,js将html生成为图片,并保存在本地

    html生成图片的方式有很多种,有的需要下载安装插件,如phantomjs .显得未免麻烦了些,有的又是通过后台来实现的,但个人感觉没必要做这种交互,也没必要在服务端生成这些零时文件,那么问题来了,有 ...

  7. Android 下载网络图片保存到本地

    通过网络地址获取网络图片,点击下载将图片显示出来,然后点击图片将图片保存到本地. 首先需要在manifest上添加一些权限: <!-- 访问网络的权限 --> <uses-permi ...

  8. 安卓 Android 下载网络图片保存到本地

    通过网络地址获取网络图片,点击下载将图片显示出来,然后点击图片将图片保存到本地. 首先需要在manifest上添加一些权限: <!-- 访问网络的权限 --> <uses-permi ...

  9. 微信小程序 canvas描绘文字图片 生成图片并保存到本地

    在实现这个功能时,遇到以下的问题: 1. canvas绘制文字的换行问题: 如果文字的长度大于你所定的宽度的话,文字会超出你所定宽度: 小程序的CanvasContext.fillText有一个max ...

最新文章

  1. CloudCompare二次开发编译篇(内附交流群更新通知)
  2. catia三维轴承_浅谈基于CATIA二次开发的单排四点接触球轴承三维设计论文
  3. c# 读hex_c#十六进制到位转换(c# hex to bit conversion)
  4. 杨超越第一,Python第二
  5. python能表示多大整数_Python无法表示99999999999999999999这样大的整数。
  6. 使用Lombok简化你的代码
  7. 应云而生,幽灵的威胁 - 云原生应用交付与运维
  8. 30美丽的矢量建筑艺术为灵感
  9. 1.0jpa 2.0_JPA 2.1类型转换器–持久枚举的更好方法
  10. 教师计算机应用研讨交流,计算机应用技术专业技能比赛研讨交流活动在济南信息工程学校举行...
  11. Error building results for action sayHello in namespace /inteceptor -
  12. 利用多线程提高程序性能(for Android)
  13. OEA 中的业务控制器设计模式
  14. HTTP服务器项目详解
  15. MongoDB学习(黑马教程)-2-数据库MongoDB的导入文档和查询文档的操作
  16. C# Chart控件
  17. C语言 编写加密程序,将用户输入的一个英文句子加密为加密字符串,然后输出加密字符串。
  18. 外观模式——透过现象看本质
  19. 安装tensorflow报错---CondaHTTPError: HTTP 000 CONNECTION FAILED for url
  20. C++三目运算符(简述)

热门文章

  1. noip普及组2004 火星人
  2. 知识图谱从哪里来:实体关系抽取的现状与未来
  3. 为什么图片img下方会出现一行空白?
  4. 什么叫动态网页?什么叫静态网页设计?
  5. [BZOJ3669]-[Noi2014]魔法森林-LCT+并查集
  6. 硬件工程师-BOOST升压电源设计
  7. 全面加速ADSL宽带速度
  8. 2010年3月23日
  9. 工程伦理--10.3 工程师的职业美德内涵
  10. 【YOLOv7改进轻量化】第一章——引入轻量化骨干网络MobileOne