草庐IT

ios - 具有永久缓存的 sdwebimage

coder 2024-01-16 原文

我正在开发一个 iOS 应用程序,它需要 SDWebImage 将图片永久缓存到手机上。

  1. SDWebImage代码中有一个过期设置,我是否应该将过期时间设置为一个较大的值以永久存储缓存?
  2. 既然我想永久缓存图片,我应该将它们存储在专用文件夹中还是默认目录就足够了?我的应用程序需要这些图片在应用程序关闭和重新打开以及手机重新启动时保持不变。
  3. 如果我想永久缓存图片,除了将到期时间设置为一个较大的值外,还有什么我需要注意的吗?

谢谢。

最佳答案

遗憾的是 SDWebImage 没有提供这样的能力 所以为了利用 SDWebImage 提供的高级缓存功能,我围绕它编写了一个包装器

基本上这个类管理一个后备永久缓存,所以如果在 SDWeb 图像缓存“磁盘和内存”中找不到请求的图像,它将在永久缓存中寻找它 如果找到永久缓存,它将被复制到 SDwebImage 缓存中,以便在以后的请求中使用其内存

使用这种方法,我设法像往常一样使用 SDWebImage 将图像设置为表格单元格 您还可以在需要时触发 clearImageCache 来清除永久缓存

.h文件

@interface CacheManager : NSObject
+ (CacheManager*)sharedManager;

// Images
- (BOOL) diskImageExistsForURL:(NSURL*) url;
- (void) downloadImage:(NSURL*) url completed:(void(^)(UIImage*))onComplete;
- (void) setImage:(NSURL*)url toImageView:(UIImageView*)iv completed:(void(^)(UIImage*))onComplete;
- (void) clearImageCache;

@end

.m 文件

#import "CacheManager.h"
#import <SDWebImage/UIImageView+WebCache.h>


#define CACH_IMAGES_FOLDER      @"ImagesData"


@implementation CacheManager


static CacheManager *sharedManager = nil;

#pragma mark -
#pragma mark Singilton Init Methods
// init shared Cache singleton.
+ (CacheManager*)sharedManager{
    @synchronized(self){
        if ( !sharedManager ){
            sharedManager = [[CacheManager alloc] init];
        }
    }
    return sharedManager;
}

// Dealloc shared API singleton.
+ (id)alloc{
    @synchronized( self ){
        NSAssert(sharedManager == nil, @"Attempted to allocate a second instance of a singleton.");
        return [super alloc];
    }
    return nil;
}

// Init the manager
- (id)init{
    if ( self = [super init] ){}
    return self;
}

/**
  @returns YES if image found in the permanent cache or the cache managed by SDWebImage lib
 */
- (BOOL) diskImageExistsForURL:(NSURL*) url{

    // look for image in the SDWebImage cache
    SDWebImageManager *manager = [SDWebImageManager sharedManager];
    if([manager diskImageExistsForURL:url])
        return YES;

    // look for image in the permanent cache
    NSString *stringPath = url.path;
    NSFileManager *fileManager = [NSFileManager defaultManager];
    return [fileManager fileExistsAtPath:stringPath];
}

/**
 get the image with specified remote url asynchronosly 
 first looks for the image in SDWeb cache to make use of the disk and memory cache provided by SDWebImage
 if not found, looks for it in the permanent cache we are managing, finally if not found in either places
 it will download it using SDWebImage and cache it.
 */
- (void) downloadImage:(NSURL*) url completed:(void(^)(UIImage*))onComplete{

    NSString *localPath = [[self getLocalUrlForImageUrl:url] path];
    NSFileManager *fileManager = [NSFileManager defaultManager];

    // -1 look for image in SDWeb cache
    SDWebImageManager *manager = [SDWebImageManager sharedManager];
    if([manager diskImageExistsForURL:url]){
        [manager downloadImageWithURL:url options:SDWebImageRetryFailed
                             progress:^(NSInteger receivedSize, NSInteger expectedSize) {}
                            completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                                onComplete(image);

                                // save the image to the perminant cache for later
                                // if not saved before
                                if(image){
                                    if ([fileManager fileExistsAtPath:localPath]){
                                        NSURL* localeUrl = [self getLocalUrlForImageUrl:url];
                                        [self saveImage:image toCacheWithLocalPath:localeUrl];
                                    }
                                }
                            }];
        return;
    }

    // -2 look for the image in the permanent cache
    if ([fileManager fileExistsAtPath:localPath]){
        UIImage *img = [self getImageFromCache:url];
        onComplete(img);
        // save image back to the SDWeb image cache to make use of the memory cache
        // provided by SDWebImage in later requests
        [manager saveImageToCache:img forURL:url];
        return;
    }

    // -3 download the image using SDWebImage lib
    [manager downloadImageWithURL:url options:SDWebImageRetryFailed
                         progress:^(NSInteger receivedSize, NSInteger expectedSize) {}
                        completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
                            onComplete(image);
                            // save the image to the permanent cache for later
                            if(image){
                                NSURL* localeUrl = [self getLocalUrlForImageUrl:url];
                                [self saveImage:image toCacheWithLocalPath:localeUrl];
                            }
                        }];

}

- (void) setImage:(NSURL*)url toImageView:(UIImageView*)iv completed:(void(^)(UIImage*))onComplete{
    [self downloadImage:url completed:^(UIImage * downloadedImage) {
        iv.image = downloadedImage;
        onComplete(downloadedImage);
    }];
}


/**
 @param:imgUrl : local url of image to read
 */
- (UIImage*) getImageFromCache:(NSURL*)imgUrl{
    return [UIImage imageWithData: [NSData dataWithContentsOfURL:imgUrl]];
}

/**
 writes the suplied image to the local path provided
 */
-(void) saveImage:(UIImage*)img toCacheWithLocalPath:(NSURL*)localPath{
    NSData * binaryImageData = UIImagePNGRepresentation(img);
    [binaryImageData writeToFile:[localPath path] atomically:YES];
}

// Generate local image URL baesd on the name of the remote image
// this assumes the remote images already has unique names
- (NSURL*)getLocalUrlForImageUrl:(NSURL*)imgUrl{
    // Saving an offline copy of the data.
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *cachesDirectory = [paths objectAtIndex:0];
    NSString *folderPath = [cachesDirectory stringByAppendingPathComponent:CACH_IMAGES_FOLDER];
    BOOL isDir;
    // create folder not exist
    if (![fileManager fileExistsAtPath:folderPath isDirectory:&isDir]){
        NSError *dirWriteError = nil;
        if (![fileManager createDirectoryAtPath:folderPath withIntermediateDirectories:YES attributes:nil error:&dirWriteError]){
            NSLog(@"Error: failed to create folder!");
        }
    }

    NSString *imgName = [[[imgUrl path] lastPathComponent] stringByDeletingPathExtension];

    NSURL *cachesDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSCachesDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil];
    NSString *pathString = [NSString stringWithFormat:@"%@/%@", CACH_IMAGES_FOLDER, imgName];
    return [cachesDirectoryURL URLByAppendingPathComponent:pathString];
}


/**
 removes the folder contating the cahced images,
 the folder will be reacreated whenever a new image is being saved to the permanent cache
 */
-(void)clearImageCache{
    // set the directory path
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *cachesDirectory = [paths objectAtIndex:0];
    NSString *folderPath =  [cachesDirectory stringByAppendingPathComponent:CACH_IMAGES_FOLDER];
    BOOL isDir;
    NSError *dirError = nil;
    // folder exist
    if ([fileManager fileExistsAtPath:folderPath isDirectory:&isDir]){
        if (![fileManager removeItemAtPath:folderPath error:&dirError])
        NSLog(@"Failed to remove folder");
    }
}

@end

关于ios - 具有永久缓存的 sdwebimage,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39642465/

有关ios - 具有永久缓存的 sdwebimage的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby - 如何验证 IO.copy_stream 是否成功 - 2

    这里有一个很好的答案解释了如何在Ruby中下载文件而不将其加载到内存中:https://stackoverflow.com/a/29743394/4852737require'open-uri'download=open('http://example.com/image.png')IO.copy_stream(download,'~/image.png')我如何验证下载文件的IO.copy_stream调用是否真的成功——这意味着下载的文件与我打算下载的文件完全相同,而不是下载一半的损坏文件?documentation说IO.copy_stream返回它复制的字节数,但是当我还没有下

  3. ruby - 如何在 Ubuntu 中清除 Ruby Phusion Passenger 的缓存? - 2

    我试过重新启动apache,缓存的页面仍然出现,所以一定有一个文件夹在某个地方。我没有“公共(public)/缓存”,那么我还应该查看哪些其他地方?是否有一个URL标志也可以触发此效果? 最佳答案 您需要触摸一个文件才能清除phusion,例如:touch/webapps/mycook/tmp/restart.txt参见docs 关于ruby-如何在Ubuntu中清除RubyPhusionPassenger的缓存?,我们在StackOverflow上找到一个类似的问题:

  4. Ruby 文件 IO 定界符? - 2

    我正在尝试解析一个文本文件,该文件每行包含可变数量的单词和数字,如下所示:foo4.500bar3.001.33foobar如何读取由空格而不是换行符分隔的文件?有什么方法可以设置File("file.txt").foreach方法以使用空格而不是换行符作为分隔符? 最佳答案 接受的答案将slurp文件,这可能是大文本文件的问题。更好的解决方案是IO.foreach.它是惯用的,将按字符流式传输文件:File.foreach(filename,""){|string|putsstring}包含“thisisanexample”结果的

  5. ruby-on-rails - Ruby on Rails 计数器缓存错误 - 2

    尝试在我的RoR应用程序中实现计数器缓存列时出现错误Unknownkey(s):counter_cache。我在这个问题中实现了模型关联:Modelassociationquestion这是我的迁移:classAddVideoVotesCountToVideos0Video.reset_column_informationVideo.find(:all).eachdo|p|p.update_attributes:videos_votes_count,p.video_votes.lengthendenddefself.downremove_column:videos,:video_vot

  6. ruby-on-rails - Rails 3.1 中具有相同形式的多个模型? - 2

    我正在使用Rails3.1并在一个论坛上工作。我有一个名为Topic的模型,每个模型都有许多Post。当用户创建新主题时,他们也应该创建第一个Post。但是,我不确定如何以相同的形式执行此操作。这是我的代码:classTopic:destroyaccepts_nested_attributes_for:postsvalidates_presence_of:titleendclassPost...但这似乎不起作用。有什么想法吗?谢谢! 最佳答案 @Pablo的回答似乎有你需要的一切。但更具体地说...首先改变你View中的这一行对此#

  7. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

  8. ruby - 具有两个参数的 block - 2

    我从用户Hirolau那里找到了这段代码:defsum_to_n?(a,n)a.combination(2).find{|x,y|x+y==n}enda=[1,2,3,4,5]sum_to_n?(a,9)#=>[4,5]sum_to_n?(a,11)#=>nil我如何知道何时可以将两个参数发送到预定义方法(如find)?我不清楚,因为有时它不起作用。这是重新定义的东西吗? 最佳答案 如果您查看Enumerable#find的文档,您会发现它只接受一个block参数。您可以将它发送两次的原因是因为Ruby可以方便地让您根据它的“并行赋

  9. ruby-on-rails - 在 RSpec 中,如何以任意顺序期望具有不同参数的多条消息? - 2

    RSpec似乎按顺序匹配方法接收的消息。我不确定如何使以下代码工作:allow(a).toreceive(:f)expect(a).toreceive(:f).with(2)a.f(1)a.f(2)a.f(3)我问的原因是a.f的一些调用是由我的代码的上层控制的,所以我不能对这些方法调用添加期望。 最佳答案 RSpecspy是测试这种情况的一种方式。要监视一个方法,用allowstub,除了方法名称之外没有任何约束,调用该方法,然后expect确切的方法调用。例如:allow(a).toreceive(:f)a.f(2)a.f(1)

  10. ruby-on-rails - 具有同名的模块和类 - 2

    我有一个模块stat存在于目录结构中:lib/stat_creator/stat/在lib/stat_creator/stat.rb中,我在lib/stat_creator/stat/目录中有我需要的文件,以及:moduleStatCreatormoduleStatendend当我使用该模块时,我将这些类称为StatCreator::Stat::Foo.new现在我想要一个存在于应用程序中的根Stat类。我在app/models中制作了我的Stat类,并在routes.rb中进行了设置。但是,如果我转到Rails控制台并尝试在应用程序/模型中使用Stat类,例如:Stat.by_use

随机推荐