【问题标题】:NSURLCache - Disk caching for GET request with parameters not workingNSURLCache - 带有参数的 GET 请求的磁盘缓存不起作用
【发布时间】:2015-02-11 17:06:08
【问题描述】:

我正在尝试使用 NSURLCache 将一些 HTTP 请求缓存到磁盘。在使用 AFNetworking 进行了一些测试但无法正常工作后,我使用 NSURLRequest 创建了一个简单的示例。

我像这样初始化 AppDelegate 上的缓存,将内存大小设置为 0,强制它始终进入磁盘:

NSUInteger sz = 1024*1024*20;
NSURLCache *cache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:sz diskPath:nil];
[NSURLCache setSharedURLCache:cache];

我使用以下代码来存储响应:

NSURLCache *urlCache = [NSURLCache sharedURLCache];

NSString *urlString = @"http://localhost:8000/cities/api/cities/?lang=en";
NSHTTPURLResponse *response = nil;
NSError *error = nil;

NSURL *finalUrl = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:finalUrl];
request.cachePolicy = NSURLRequestReturnCacheDataElseLoad;

NSData *data = nil;

data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];

NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[urlCache storeCachedResponse:cachedResponse forRequest:request];

我的请求具有 HTTP Header Cache-Control 之类的:

response['cache-control'] = 'max-age=36000, public'

我看到响应缓存在文件 Cache.db 中。

然后,在下一次执行甚至同一次执行中,我使用以下代码尝试从缓存中获取响应:

NSCachedURLResponse *cachedResponse = [urlCache cachedResponseForRequest:request];
response = cachedResponse.response;
data = cachedResponse.data;

问题在于,当请求上有 GET 参数时,cachedResponse 始终为空。如果请求为“http://localhost:8000/cities/api/cities/”,则存储结果,稍后恢复。但是当它确实有 GET 参数时,某些东西会阻止方法 [NSURLCache cachedResponseForRequest:request] 找到请求。

鉴于此,我将 NSURLCache 子类化并像这样实现该方法:

- (id)initWithMemoryCapacity:(NSUInteger)memoryCapacity diskCapacity:(NSUInteger)diskCapacity diskPath:(NSString *)path {
self = [super initWithMemoryCapacity:memoryCapacity diskCapacity:diskCapacity diskPath:path];
if (self) {
    NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *dir = (NSString*)[docPaths objectAtIndex:0];
    dir = [dir stringByAppendingString:@"/uk.co.airsource.TestURLCache/nsurlcache"];
    NSString *path = [dir stringByAppendingFormat:@"/Cache.db"];
    self.db = [[FMDatabase alloc] initWithPath:path];
    [self.db open];
    int o = 4;
}
return self;

}

- (NSCachedURLResponse *) cachedResponseForRequest:(NSURLRequest *)request {
NSCachedURLResponse *cachedURLResponse = [super cachedResponseForRequest:request];
if (cachedURLResponse == nil && [request.HTTPMethod isEqualToString:@"GET"]) {
    //[db executeQuery:@"select * from test where a = ?", @"hi'",  nil];
    NSLog(@"%@", request.URL.absoluteString);
    FMResultSet *cfurl_cache_response = [self.db executeQuery:@"select * from cfurl_cache_response where request_key = ? limit 1", request.URL.absoluteString, nil];
    if ([cfurl_cache_response next]) {
        id entry_ID = [cfurl_cache_response objectForColumnName:@"entry_ID"];
        [cfurl_cache_response close];
        if (entry_ID != [NSNull null]) {
            FMResultSet *cfurl_cache_blob_data = [self.db executeQuery:@"select * from cfurl_cache_blob_data where entry_ID = ? limit 1", entry_ID, nil];
            if ([cfurl_cache_blob_data next]) {
                id response_object = [cfurl_cache_blob_data objectForColumnName:@"response_object"];
                [cfurl_cache_blob_data close];
                FMResultSet *cfurl_receiver_data = [self.db executeQuery:@"select * from cfurl_cache_receiver_data where entry_ID = ? limit 1", entry_ID, nil];
                if ([cfurl_receiver_data next]) {
                    id receiver_data = [cfurl_receiver_data objectForColumnName:@"receiver_data"];
                    [cfurl_receiver_data close];
                    if (response_object != [NSNull null] && receiver_data != [NSNull null] && response_object && receiver_data) {
                        NSURLResponse *urlResponse = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:[[request allHTTPHeaderFields] objectForKey:@"Accept"] expectedContentLength:[(NSData *)response_object length] textEncodingName:nil];
                        cachedURLResponse = [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:receiver_data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
                    }
                }
            }
        }
    }
}
return cachedURLResponse;

}

在这种情况下,在带有参数的 GET 请求下,它确实遵循每一行,直到获得之前缓存的 NSURLResponse。

当请求有GET参数时为什么不能正常工作有什么想法吗?

【问题讨论】:

    标签: ios objective-c nsmutableurlrequest nsurlcache


    【解决方案1】:

    问题似乎是没有指定内存容量,如果你指定内存容量如下,

    这里我将内存容量指定为 1 MB 的大小

    [[NSURLCache alloc] initWithMemoryCapacity:1*1024*1024 diskCapacity:sz diskPath:nil];
    

    它会起作用,在我看来好像需要内存容量,无论容量多小但不能为空。然后您将能够提取存储的缓存如下,

    NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
    response = cachedResponse.response;
    data = cachedResponse.data;
    

    【讨论】:

    • 我重新启动了一切并按照你说的做了,但同样的事情正在发生。响应存储在数据库中。然后我关闭应用程序并再次打开它,当我尝试从缓存中获取响应时,它返回 nil。
    • 在我看来好像根本不会检查 Cache.db 中的磁盘缓存
    • 能否确认您的本地网址是否返回内容?
    • 在我看来好像您的本地服务器没有设置缓存头?如果是这种情况,那么你需要在客户端指定一个
    • 您可以通过将 URL 更改为 google one,www.google.com 作为 google one set cache headers 来确认。
    【解决方案2】:

    最后我重写了 NSURLCache 来实现我想要的:

    1. GET 请求(带参数)缓存到磁盘
    2. 当“Cache-Control max-age”值表示未过期时请求缓存,并在过期后再次进入服务器,存储新响应并开始过期时间
    3. 如果离线,总是从缓存返回响应(如果存在)

    NTURLCache - overrides NSURLCache

    我严重认为这是在 iOS 8.1 中无法正常工作的问题

    【讨论】:

    • 我通过继承 NSURLCache 解决了这个问题,您可以在我的回复中的链接上找到该项目。
    【解决方案3】:

    iOS 11.x 的问题仍然存在,尽管我有一个 iPad Mini 运行版本iOS 9.3.5,它具有完美的缓存。 NSURLCache/URLCache 确实有问题

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2017-03-10
      • 2014-02-13
      • 2013-04-27
      • 2018-02-05
      • 2011-12-15
      • 1970-01-01
      相关资源
      最近更新 更多