SDWebImage支持URL不變時更新圖片內容

有一些開發者可能會遇到一種狀況,當服務器端的圖片改變而url沒改變的時候,客戶端的圖片不會更新成最新的圖片。這是爲何呢,往下看 -->緩存

方法一:服務器

一、curl

咱們來簡單探討一下SDWebImage的實現原理: SDWebImageManager內部利用SDWebImageDownloader來下載,它的緩存策略有兩種,一種是用NSURL緩存,一種是本身定義了SDImageCache(內部使用NSCache)進行緩存。 若是設置了SDWebImageRefreshCached標示位,那麼SDWebImageDownloader則利用NSURL進行緩存,並且使用的policy爲NSURLRequestUseProtocolCachePolicy。 那麼若是設置了SDWebImageRefreshCached標識位,圖片是否更新則要取決於你服務器的cache-control設置了,若是沒有cache-control的話,客戶端則然享受不了自動更新的功能。 因此說僅僅設置SDWebImageRefreshCached每每是不能解決問題的。。。。 那麼如何查看服務器是否支持cache-control呢? 其實簡單,只須要要終端輸入url

curl [url] --head spa

這就能夠了。

二、code

基於這一現象,咱們來進行分析。 客戶端第一次請求圖片時,Charles抓包得知response header裏有一個名爲Last-Modified、數據是時間戳的鍵值對。orm

客戶端第二次及之後請求圖片時,經過Charles抓包發現,服務器返回304 not modified狀態,說明服務器在接收客戶端請求後經過某種判斷邏輯得出結論:「客戶端已緩存的圖片與服務器圖片都是最新的」,那麼服務器如何判斷的呢?cdn

經過查閱HTTP協議相關的資料得知,與服務器返回的Last-Modified相對應的request header裏能夠加一個名爲If-Modified-Since的key,value便是服務器回傳的服務端圖片最後被修改的時間,第一次圖片請求時If-Modified-Since的值爲空,第二次及之後的客戶端請求會把服務器回傳的Last-Modified值做爲If-Modified-Since的值傳給服務器,這樣服務器每次接收到圖片請求時就將If-Modified-Since與Last-Modified進行比較,若是客戶端圖片已陳舊那麼返回狀態碼200、Last-Modified、圖片內容,客戶端存儲Last-Modified和圖片;若是客戶端圖片是最新的那麼返回304 Not Modified、不會返回Last-Modified、圖片內容。blog

關於服務器的比較邏輯,須要強調一下。圖片

經查資料得知,Apache比較時是看If-Modified-Since以後有沒有更新圖片,Nginx比較時是看If-Modified-Since與Last-Modified是否相等,因此對於Apache服務器環境客戶端每次都要嚴格的存儲服務器回傳的Last-Modified以便下次請求時做爲If-Modified-Since的值傳給服務器,對於Nginx服務器環境客戶端沒必要存儲服務器回傳的Last-Modified,每次請求時只需將圖片自身的fileModificationDate做爲If-Modified-Since的值傳服務器便可。在實際開發中,若是遇到明明傳了If-Modified-Since、服務器圖片也變動了、可是客戶端卻請求不到最新的圖片的狀況時,那麼就須要查看一下服務器對這兩個時間戳的比較邏輯。

那麼,如今咱們能夠回到SDWebImage上來了。經過查看SDWebImageDownloader的源碼得知,它開放了一個headersFilter的block,意在讓開發者能夠對全部圖片請求追加一些額外的header,這正合我意。那麼咱們就能夠在諸如AppDelegate didFinishLaunching的地方追加以下代碼:

SDWebImageDownloader *imgDownloader = SDWebImageManager.sharedManager.imageDownloader; imgDownloader.headersFilter = ^NSDictionary *(NSURL *url, NSDictionary *headers) {

NSFileManager *fm = [[NSFileManager alloc] init];
NSString *imgKey = [SDWebImageManager.sharedManager cacheKeyForURL:url];
NSString *imgPath = [SDWebImageManager.sharedManager.imageCache defaultCachePathForKey:imgKey];
NSDictionary *fileAttr = [fm attributesOfItemAtPath:imgPath error:nil];

NSMutableDictionary *mutableHeaders = [headers mutableCopy];

NSDate *lastModifiedDate = nil;

if (fileAttr.count > 0) {
    if (fileAttr.count > 0) {
        lastModifiedDate = (NSDate *)fileAttr[NSFileModificationDate];
    }

}
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US"];
formatter.dateFormat = @"EEE, dd MMM yyyy HH:mm:ss z";

NSString *lastModifiedStr = [formatter stringFromDate:lastModifiedDate];
lastModifiedStr = lastModifiedStr.length > 0 ? lastModifiedStr : @"";
[mutableHeaders setValue:lastModifiedStr forKey:@"If-Modified-Since"];

return mutableHeaders;
複製代碼

};

而後,加載圖片的地方之前怎麼寫仍是怎麼寫,但別忘了Option是SDWebImageRefreshCached

NSURL *imgURL = [NSURL URLWithString:@"handy-img-storage.b0.upaiyun.com/3.jpg"];

[[self imageView] sd_setImageWithURL:imgURL placeholderImage:nil options:SDWebImageRefreshCached];

方法二:

在SDWebImageManager.m大約167行的

if (cachedImage && options & SDWebImageRefreshCached) {

// force progressive off if image already cached but forced refreshing
    downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
     // ignore image read from NSURLCache if image if cached but force refreshing
     downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
複製代碼

}

中間加上:

// remove SDWebImageDownloaderUseNSURLCache flag downloaderOptions &= ~SDWebImageDownloaderUseNSURLCache;

變成:

if (cachedImage && options & SDWebImageRefreshCached) {

            // force progressive off if image already cached but forced refreshing
            downloaderOptions &= ~SDWebImageDownloaderProgressiveDownload;
            // remove SDWebImageDownloaderUseNSURLCache flag
            downloaderOptions &= ~SDWebImageDownloaderUseNSURLCache;
            // ignore image read from NSURLCache if image if cached but force refreshing
            downloaderOptions |= SDWebImageDownloaderIgnoreCachedResponse;
        }複製代碼
相關文章
相關標籤/搜索