使用storeCachedResponse存储在缓存中后,不会检索URLresponse

Hon*_*ney 5 caching http nsurlcache ios nsurlsession

目标

我正在尝试将URLRequest中的数据/响应注入我的缓存中的另一个 URLRequest.

建立

这只是一个示例代码.它已准备好被转储到一个项目中.

我正在尝试做的是使用从我的landscapeURLString网络请求中检索到的响应+数据...存储到我的会话缓存中以获取我的lizardURLString请求.

import UIKit

class ViewController: UIViewController {

    lazy var defaultSession : URLSession = {
        let urlCache = URLCache(memoryCapacity: 500 * 1024 * 1024, diskCapacity: 500 * 1024 * 1024, diskPath: "something")
        let configuration = URLSessionConfiguration.default
        configuration.urlCache = urlCache
        let session = URLSession(configuration: configuration)

        return session
    }()
    lazy var downloadLizzardbutton : UIButton = {
        let btn = UIButton()
        btn.translatesAutoresizingMaskIntoConstraints = false
        btn.setTitle("download lizard image OFFLINE", for: .normal)
        btn.backgroundColor = .blue
        btn.addTarget(self, action: #selector(downloadLizardAction), for: .touchUpInside)
        return btn
    }()

    let imageView : UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill
        return imageView
    }()

    // I make sure my internet is set to OFF so that it forces this to be read from cache...
    @objc func downloadLizardAction() {
        downloadImage(from: lizardURLString, from: defaultSession)
    }
    let lizardURLString = "https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg"
    let landscapeURLString = "https://images.pexels.com/photos/414171/pexels-photo-414171.jpeg"        

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(imageView)
        view.addSubview(downloadLizzardbutton)
        imageView.pinToAllEdges(of: view)

        downloadImage(from: landscapeURLString, from: defaultSession)
    }
    private func downloadImage(from urlString: String, from session : URLSession){
        guard let url = URL(string: urlString) else{
            fatalError("bad String we got!")
        }

        let urlRequest = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 15)
        print("url.hashValue: \(urlRequest.hashValue)")

        let task = session.dataTask(with: urlRequest) { [weak self] (data, response, error) in

            guard error == nil else {
                print(error)
                return
            }
            guard let httpResponse = response as? HTTPURLResponse,
                (200...299).contains(httpResponse.statusCode) else {
                    print("response NOT 2xx: \(response)")
                    return
            }

            for header in httpResponse.allHeaderFields{
                if let key = header.key as? String, key == "Cache-Control"{
                    print("found Cache-Control: \(httpResponse.allHeaderFields["Cache-Control"])")
                }
            }

            if let data = data,
                let image = UIImage(data: data){
                let lizardURL = URL(string: self!.lizardURLString)
                let lizardURLRequest = URLRequest(url: lizardURL!)

                let landscapeCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
                print("before storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")

                session.configuration.urlCache?.storeCachedResponse(landscapeCachedURLPResponse, for: lizardURLRequest)    

                print("after storing into cache: \(String(describing: session.configuration.urlCache?.cachedResponse(for: lizardURLRequest)))")
                print("lizardRequest.hashValue: \(lizardURLRequest.hashValue)")

                DispatchQueue.main.async {
                    self?.imageView.image = image
                }
            }
        }
        task.resume()
    }        
}


extension UIView{

    func pinToAllEdges(of view: UIView){
        let leading = leadingAnchor.constraint(equalTo: view.leadingAnchor)
        let top = topAnchor.constraint(equalTo: view.topAnchor)
        let trailing = trailingAnchor.constraint(equalTo: view.trailingAnchor)
        let bottom = bottomAnchor.constraint(equalTo: view.bottomAnchor)

        NSLayoutConstraint.activate([leading, top, trailing, bottom])
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经验证过的事情:

  • landscapeURLStringcache-control一个头max-age31536000
  • 如果它是全新安装,那么存储到缓存之前,我cachedResponse的lizardURLString是nil.但是存储之后,它已经不存在了nil.结果我得出结论,我已成功将某些内容存储到缓存中!
  • 我还怀疑URLCache认为URLRequest是关键.所以我打印了我的hashValue lizardURLString.它与我存储的密钥相同.结合上述观点,我得出结论,缓存中存在确切的密钥!
  • 我还可以看到,当我将它存储在缓存中时,我的currentMemoryUsage增加.

我是如何测试的以及我所看到的:

  1. 我只是下载了风景图片.
  2. 关掉我的网络
  3. 单击按钮下载蜥蜴图像.

显然它是离线的.我希望它可以从缓存中使用,但事实并非如此.我得到的只是一个时间!

我也尝试过更改cachePolicyreturnCacheDataElseLoad,但这也无济于事

EDIT1:

我也尝试过大卫所说和所做的事:

let landscapeHTTPResponse : HTTPURLResponse = HTTPURLResponse(url: self!.lizardURL, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: (httpResponse.allHeaderFields as! [String : String]))!
let landscapedCachedURLPResponse : CachedURLResponse = CachedURLResponse(response: landscapeHTTPResponse, data: data, userInfo:nil, storagePolicy: .allowed)
Run Code Online (Sandbox Code Playgroud)

并存储landscapedCachedURLPResponse到缓存中.那也行不通.它也超时 - 它并不是每个人都看到缓存.

EDIT2:

所以我取得了一些进展.或者可能退后一步,前进一步.

我试着看看是否可以存储相同 URL 的响应,看看我是否可以在清空缓存后检索响应.我无法做到.

我正在创建我的缓存响应,如下所示:

let cachedResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
Run Code Online (Sandbox Code Playgroud)

或者就像这样:

let cachedResponse = CachedURLResponse(response: response!, data: data)
Run Code Online (Sandbox Code Playgroud)

是什么让这部分工作?:

let cachedResponseFromCache = session.configuration.urlCache?.cachedResponse(for: self!.landscapeURLRequest)
self._cachedResponse = cachedResponseFromCache
Run Code Online (Sandbox Code Playgroud)

然后我:

  1. 刷新缓存
  2. 关闭互联网
  3. 试图下载图像,但没有成功,这是好的.这是预期的行为
  4. 存储cachedResponseFromCache属性到缓存中.
  5. 能够从缓存中检索!

我不确定从缓存本身到创建缓存之间的区别是什么Response + Data.

这很重要,因为我开始质疑URLCache中是否还存在某种形式的内部错误.这使我有理由相信它可能按预期工作.

现在我知道存储到缓存的过程.我知道我的URLResponse很好.我只需要通过映射URLRequest来完成工作

EDIT3:

Guy Kogus建议我的网址必须来自同一个来源.所以,一旦我下载了他提到的熊图像,我的lizardImage就会通过.瞧!

作为我学到的非常重要的调试说明:即使您在问题的某些部分(它正在缓存景观图像)上获得成功,更改变量(此处更改初始URL)也可以始终更改整个测试结果.

他怀疑这是因为Server在标题中共享,这对查找cachedResponse非常重要.

我反驳说,我的lizardURLRequest是在网上制作的,所以没有什么可比的,但它有效!所以接下来的想法是它可能与URL的某些部分有关,比如它的第一段或其他部分.

那么我去改变了lizardURL:

https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg

类似于:https: //skdhfsupload.qwiklkjlkjimedia.com/qwikipehkjdia/eeeeeecommons/sdalfjkdse/aldskfjae0/extraParam/anotherextraparam/asasdLarge_Scaled_Forest_Lizard.jpeg

我在URL中添加了哑字符.我还在其中添加了额外的细分.我最后更改了文件类型.

它仍然有效.因此,我唯一可以得出的结论是,Headers中的某些东西正在做决策.

我的landscapeURL的标题是:(缓存另一个URL不适用于此)

Content-Length : 997361
x-cache : HIT, MISS
cf-ray : 472793e93ce39574-IAD
x-served-by : cache-lax8621-LAX, cache-iad2132-IAD
cf-cache-status : HIT
Last-Modified : Sun, 14 Oct 2018 2:10:05 GMT
Accept-Ranges : bytes
Vary : Accept-Encoding
x-content-type-options : nosniff
Content-Type : image/jpeg
expect-ct : max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Set-Cookie : __cfduid=d5f5fd59ce5ff9ac86e42f8c008708ae61541004176; expires=Thu, 31-Oct-19 16:42:56 GMT; path=/; domain=.pexels.com; HttpOnly
Expires : Thu, 31 Oct 2019 16:42:56 GMT
Server : cloudflare
Cache-Control : public, max-age=31536000
Date : Wed, 31 Oct 2018 16:42:56 GMT
Run Code Online (Sandbox Code Playgroud)

我的BearURL的标题是:(缓存另一个URL适用于此)

Date : Wed, 31 Oct 2018 16:46:38 GMT
Content-Length : 215104
x-client-ip : 2001:558:1400:4e:808c:2738:43e:36f5
access-control-expose-headers : Age, Date, Content-Length, Content-Range, X-Content-Duration, X-Cache, X-Varnish
x-cache : cp1076 miss, cp1088 hit/21
Age : 27646
Etag : 00e21950bf432476c91b811bb685b6af
Strict-Transport-Security : max-age=106384710; includeSubDomains; preload
x-analytics : https=1;nocookies=1
Accept-Ranges : bytes
x-object-meta-sha1base36 : 42tq5grg9rq1ydmqd4z5hmmqj6h2309
x-varnish : 48388488, 503119619 458396839
x-cache-status : hit-front
Content-Type : image/jpeg
x-trans-id : tx08ed43bbcc1946269a9a3-005bd97070
Last-Modified : Fri, 04 Oct 2013 23:30:08 GMT
Access-Control-Allow-Origin : *
timing-allow-origin : *
x-timestamp : 1380929407.39127
Via : 1.1 varnish (Varnish/5.1), 1.1 varnish (Varnish/5.1)
Run Code Online (Sandbox Code Playgroud)

重要的提示:

对于BearURL,可以缓存BearURL lizardURL或任何其他URL.对于landscapeURL,缓存仅适用于landscapeURL本身.它不适用于任何其他 URL.

因此,问题的当前状态是:需要包含哪些标头才能实现此功能?

dga*_*ood 3

欢迎来到异步缓存的奇妙世界。NSURLCache 是高度异步的。仅仅因为您已将数据推入其中并不意味着它可供检索。您必须让主运行循环返回才能可用,甚至可能要等待一段时间。存储响应后未能立即返回响应的情况并不罕见。尝试在五秒左右后发送它。

其次,您的缓存可能有点小,无法存储多兆字节的图像。尝试提高它,看看是否有帮助。

最后,当您说“关闭互联网”时,您的意思是什么?你说你要超时。通常,如果您将设备置于飞行模式并禁用所有连接,则在失败并出现指示无连接的错误之前,它不应停留在该状态很长时间。如果没有发生这种情况,就会发生一些奇怪的事情,几乎就像在会话上设置了 waitsForConnectivity 或其他东西一样。(您没有在后台发出网络请求,是吗?如果是这样,请尝试将 waitsForConnectivity 显式设置为 NO,这样它们就不会等待可用的连接。)

此外,对于这种用法,您可能必须删除 Vary: Accept-Encoding 标头或提供一致的用户代理字符串。该标头导致缓存基本上是针对每个浏览器的。这可能会导致缓存以意想不到的方式运行,并且可能是您所看到的奇怪现象的原因。

请注意,删除 Vary 标头有点麻烦,而且可能不是解决问题的最正确方法;理想情况下,您应该调整必须调整的任何传出标头字段,以便即使存在该标头也能正常工作。但你必须对其进行研究并弄清楚到底需要哪些字段,因为我不知道。:-)