duyojiぶろぐ

技術系ときどき日常系

一度ネットから取得した画像データを端末内にキャッシュを貯める

取り敢えずソースコード

#define USER_ICON 0
#define APP_ICON 1
#define SCREEN_SHOT 2

@implementation Cache 
//--------------------------------------------------------------------------------------------
// キャッシュ削除関連
//--------------------------------------------------------------------------------------------

//全キャッシュ削除
+ (BOOL) deleteCacheDirectory {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *dir = [paths objectAtIndex:0];
    NSString *cacheDir = [dir stringByAppendingPathComponent:@"cache"];    
    return [[NSFileManager defaultManager] removeItemAtPath:cacheDir error:nil];
}

// アプリアイコンのキャッシュを削除
+ (BOOL) deleteAppIconCacheDirectory {
    NSString *appIconDir = [self getCacheDir:APP_ICON];
    return [[NSFileManager defaultManager] removeItemAtPath:appIconDir error:nil];
}

// ユーザーアイコンのキャッシュを削除
+ (BOOL) deleteUserIconCacheDirectory {
    NSString *userIconDir = [self getCacheDir:USER_ICON];
    return [[NSFileManager defaultManager] removeItemAtPath:userIconDir error:nil];
}

// スクリーンショットのキャッシュを削除
+ (BOOL) deleteScreenShotCacheDirectory {
    NSString *screenShotDir = [self getCacheDir:SCREEN_SHOT];
    return [[NSFileManager defaultManager] removeItemAtPath:screenShotDir error:nil];
}

//---------------------------------------------------------------------------------------------

//キャッシュディレクトリを取得
+(NSString*)getCacheDir:(int)flag{     
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);        
    //Library/Cachesまでのpath
    NSString *dir = [paths objectAtIndex:0];
    
    // オリジナル/Library/Caches/cacheまでのパスのディレクトリを作る
    NSString *cacheDir = [dir stringByAppendingPathComponent:@"cache"];        
    NSString *fileDir = nil;

  ///Library/Caches/cache/User or App or SecreenShot までのパスを取得
    if (flag == USER_ICON) 
        fileDir = [cacheDir stringByAppendingPathComponent:@"User"];
    else if(flag == APP_ICON) 
        fileDir = [cacheDir stringByAppendingPathComponent:@"App"];
    else 
        fileDir = [cacheDir stringByAppendingPathComponent:@"ScreenShot"];
    
    return fileDir;
}


+(NSString*)getCacheDir:(int)flag fileName:(NSString*)_fileName{ 
    NSFileManager *fm = [NSFileManager defaultManager];
    NSError *error;
    
    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    
    NSString *dir = [paths objectAtIndex:0];
    
    // オリジナル/Library/Caches/cacheまでのパスのディレクトリを作る
    NSString *cacheDir = [dir stringByAppendingPathComponent:@"cache"];    
    if (![fm fileExistsAtPath:cacheDir]) {
        [fm createDirectoryAtPath:cacheDir withIntermediateDirectories:YES attributes:nil error:&error];
    }
    
    NSString *fileDir = nil;

    ///Library/Caches/cache/User or App or SecreenShot までのパスを取得
    if (flag == USER_ICON) 
        fileDir = [cacheDir stringByAppendingPathComponent:@"User"];
    else if(flag == APP_ICON) 
        fileDir = [cacheDir stringByAppendingPathComponent:@"App"];
    else 
        fileDir = [cacheDir stringByAppendingPathComponent:@"ScreenShot"];
    
    // fileDirに保存したディレクトリパスが作られていなければ作る
    if (![fm fileExistsAtPath:fileDir]) {
        [fm createDirectoryAtPath:fileDir withIntermediateDirectories:YES attributes:nil error:&error];
    }
    
  // http://hoge/fuga/test.pngを[hoge, fuga, test.png]のように配列にする
    NSArray* httpPathArray = [_fileName componentsSeparatedByString:@"/"];
    
    NSString *imageDir = nil;
    if (flag == USER_ICON) {
      //ユーザーアイコンは同じ画象名がある可能性があるから後ろから2番目のパスをディレクトリ名にする
        imageDir = [httpPathArray objectAtIndex:httpPathArray.count-2];
    }else {
        //ファイル名の最初の3文字をディレクトリ名にする
        NSString *imageFileName = [httpPathArray objectAtIndex:httpPathArray.count-1];
        imageDir = [self getSubstring3Dir:imageFileName];
    }
    
    imageDir = [fileDir stringByAppendingPathComponent:imageDir];    
    if (![fm fileExistsAtPath:imageDir]) {
        [fm createDirectoryAtPath:imageDir withIntermediateDirectories:YES attributes:nil error:&error];
    }    
    return imageDir;        
}


+(NSString*)getSubstring3Dir:(NSString*)fileName{
   return [fileName substringToIndex:3];
}

//flagは画象の種類(ユーザーアイコン,アプリアイコン,スクリーンショット)によってキャッシュを入れるディレクトリを決める
+(void)setFileData:(NSString*)fileName data:(NSData*)fileData indexFlag:(int)flag{        
    
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *fileDirPath = [self getCacheDir:flag fileName:fileName];  

    NSArray* httpPathArray = [fileName componentsSeparatedByString:@"/"];
    NSString *imageFile = [httpPathArray objectAtIndex:httpPathArray.count-1];
    
    NSString *filePath = [fileDirPath stringByAppendingPathComponent:imageFile];
    
    if (![fm fileExistsAtPath:filePath]) {
        [fm createFileAtPath:filePath contents:fileData attributes:nil];
    }
}


//画象URLをfileNameに画象の種類によってflagを切り替える
+(void)setFileData:(NSString*)fileName indexFlag:(int)flag{            

  //画象取得
    NSURLRequest *request = [NSURLRequest requestWithURL:
                             [NSURL URLWithString:[fileName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]
                                             cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
    
    NSURLResponse *response;
    NSError       *error;
    NSData *resultData = [NSURLConnection sendSynchronousRequest:request 
                                   returningResponse:&response error:&error];
    
    
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *fileDirPath = [self getCacheDir:flag fileName:fileName];
    
    NSArray* httpPathArray = [fileName componentsSeparatedByString:@"/"];
    NSString *imageFile = [httpPathArray objectAtIndex:httpPathArray.count-1];
    
    NSString *filePath = [fileDirPath stringByAppendingPathComponent:imageFile];

  //キャッシュが作られていなければ取得した画象をキャッシュに入れる
    if (![fm fileExistsAtPath:filePath]) {
        [fm createFileAtPath:filePath contents:resultData attributes:nil];
    }
    
}


//キャッシュファイルがあったらキャッシュ画象を返す
+(NSData*)getFileData:(NSString*)fileName indexFlag:(int)flag{
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *fileDirPath = [self getCacheDir:flag fileName:fileName];
    NSArray* httpPathArray = [fileName componentsSeparatedByString:@"/"];
    NSString *imageFile = [httpPathArray objectAtIndex:httpPathArray.count-1];    
    NSString *filePath = [fileDirPath stringByAppendingPathComponent:imageFile];    
    
    NSData* data;
    if ([fm fileExistsAtPath:filePath]) {        
        data = [NSData dataWithContentsOfFile:filePath];    
    }    
    return data;
}

//キャッシュがあるかチェック
+(BOOL)isExistCache:(NSString*)fileName indexFlag:(int)flag{    
    NSFileManager *fm = [NSFileManager defaultManager];
    NSString *fileDirPath = [self getCacheDir:flag fileName:fileName];    
    NSArray* httpPathArray = [fileName componentsSeparatedByString:@"/"];
    NSString *imageFile = [httpPathArray objectAtIndex:httpPathArray.count-1];    
    NSString *filePath = [fileDirPath stringByAppendingPathComponent:imageFile];    
    return [fm fileExistsAtPath:filePath];
}


@end

説明

いろんなところで出てくるint型でflagを用意しているのは
キャッシュする画象の種類(アプリアイコン, ユーザーアイコン, スクリーンショット)に
よってディレクトリを切り替えるため。それぞれのディレクトリ(App, User, Screenshot)は
キャッシュする期間が異なるようにしたかったためディレクトリを分けた。

キャッシュ削除

NSUserDefaultなどでNSDateを使ってキャッシュを削除したい時間を設定するなどして
設定した時間が来たら「deleteCacheDirectory」関連を呼び出すと端末内に保存した
キャッシュを削除することが出来る。

画象をキャッシュする

データをまだ取得していないときは「+(void)setFileData:(NSString*)fileName indexFlag:(int)flag」を
呼び出すと一度ネットワーク接続をしてデータを取得してからキャッシュを保存する。
すでにデータを取得しているのであれば「+(void)setFileData:(NSString*)fileName data:(NSData*)fileData indexFlag:(int)flag」を
呼び出すと保存する。

キャッシュしているかどうか判断する

「+(BOOL)isExistCache:(NSString*)fileName indexFlag:(int)flag」で画象URLと
アプリアイコンか、ユーザーアイコン、スクリーンショットかflagをセットしたら
キャッシュしているか判断する。
これを使ってファイルを保存するかしないか判断すると良いかもしれない。

キャッシュ画象を取得

「+(NSData*)getFileData:(NSString*)fileName indexFlag:(int)flag」で
画象URLをfileNameにアプリアイコンか、ユーザーアイコン、スクリーンショットかflagをセットしたら
キャッシュがある場合その画象を返す。

最後に

App, User, ScreenShotは今回僕が作るプロジェクトで
使うことになっただけで、実際にこのコードを使う際は
それぞれ環境に合わせて書き換えて使って頂けたらと思います。