使用 storeCachedResponse 存储在缓存中后,不会检索 URLresponse [英] URLresponse is not retrieved after storing in cache using storeCachedResponse

查看:22
本文介绍了使用 storeCachedResponse 存储在缓存中后,不会检索 URLresponse的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to inject data/response from URLRequest into another URLRequest in my cache.

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

This is just a sample code. It's ready to be dumped into a project.

我想要做的是使用从我的 landscapeURLString 网络请求中检索到的响应 + 数据...存储到我的会话缓存中用于我的 lizardURLString 请求.

What I'm trying to do is use the response + data retrieved from my landscapeURLString network request...store into my session's cache for my lizardURLString request.

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])
    }
}

我已经验证过的事情:

  • 我的 landscapeURLString 有一个 cache-control 标头,max-age31536000
  • 如果是全新安装,那么存储到缓存之前,lizardURLString的cachedResponsenil.但是存储之后,它不再是nil.结果我得出结论,我成功地将一些东西存储到缓存中!
  • 我还怀疑 URLCache 将 URLRequest 视为关键.所以我打印了我的 lizardURLString 的 hashValue.它与我存储的密钥相同.结合上述观点,我得出结论,缓存中存在确切的键!
  • 我还可以看到,当我将它存储在缓存中时,我的 currentMemoryUsage 会增加.
  • Things I've already validated:

    • My landscapeURLString has a cache-control header with a max-age of 31536000
    • If it's a fresh install, then before storing into the cache, my cachedResponse for lizardURLString is nil. But after storing, it's no longer nil. As a result I conclude that I'm successfully storing something into the cache!
    • I also suspect the URLCache considers the URLRequest as the key. So I printed the hashValue of my lizardURLString. It's same as the key I've stored. Combining that with the point above, I concluded that exact key exists in cache!
    • I can also see that when I store it in my cache, my currentMemoryUsage increases.
      1. 我只是下载风景图片.
      2. 关闭我的互联网
      3. 点击按钮下载蜥蜴图片.

      显然它处于离线状态.我希望它从缓存中使用,但它没有.我得到的只是超时!

      Obviously it's offline. I expect it to use from the cache but it doesn't. All I get is a time out!

      我也尝试将 cachePolicy 更改为 returnCacheDataElseLoad,但这也无济于事

      I also tried changing the cachePolicy to returnCacheDataElseLoad, but that didn't help either

      编辑 1:

      我也尝试过按照大卫所说的去做:

      I also tried doing what David said and do:

      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)
      

      和存储的 landscapedCachedURLPResponse 到缓存中.那也没有用.它也会超时——它不会每次都查看缓存.

      and the stored landscapedCachedURLPResponse into the cache. That didn't work either. It times out as well — it doesn't every look into the cache.

      所以我取得了一些进展.或者也许退后一步,再前进一步.

      So I made some progress. Or perhaps took one step back and one step forward.

      我尝试查看是否可以存储 相同 URL 的响应,并在清空缓存后查看是否可以检索响应.我没办法.

      I tried to see if I can store the response for the same URL and see if I can retrieve the response after I empty my cache. I wasn't able to.

      我正在像这样创建我的缓存响应:

      I was creating my cached response like this:

      let cachedResponse = CachedURLResponse(response: response!, data: data, userInfo:nil, storagePolicy: .allowed)
      

      或者就这样:

      let cachedResponse = CachedURLResponse(response: response!, data: data)
      

      是什么让这部分工作?:

      let cachedResponseFromCache = session.configuration.urlCache?.cachedResponse(for: self!.landscapeURLRequest)
      self._cachedResponse = cachedResponseFromCache
      

      然后我:

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

      我不确定从缓存本身中提取和从 Response + Data 创建缓存之间有什么区别.

      I'm not sure what's the difference between pulling off from cache itself and creating the cache from Response + Data.

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

      This is important because I was starting to question if there are still some form of internal bugs in URLCache. This has given me reason to believe that it may be working as expected.

      现在我知道存储到缓存中的过程是有效的.我知道我的 URLResponse 很好.我只需要按照自己的方式映射 URLRequest

      Now I know the process of storing into cache works. I know my URLResponse is good. I just need to work my way through mapping the URLRequest

      Guy Kogus 建议我的 URL 必须来自同一来源.所以一旦我下载了他提到的 bearImage,我的 lizardImage 就通过了.瞧!

      Guy Kogus suggested that my URLs need to be from the same source. So once I downloaded the bearImage he mentioned, my lizardImage was coming through. VOILA!

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

      As very important debugging note that I learned: Even if your getting success on some part (that it was caching the landscape image for itself) of the problem, changing variables (here changing the initial URL) can always change the entire testing results.

      他怀疑这是因为共享头中的Server,这对于查找cachedResponse 很重要.

      He suspected that it was because the Server in header in shared and that's important for looking up the cachedResponse.

      我反驳了这个说法,说我的 lizardURLRequest 是在它在线时发出的,所以没有什么可比的,但它有效!所以下一个想法是它可能与 URL 的某些部分有关,比如它的第一段或其他东西.

      I refuted that claim by saying that my lizardURLRequest is made when it's online so there's nothing for it compare with, yet it works! So the next idea was that it may have something to do with some part of the URL, like it's first segment or something.

      然后我去修改了lizardURL:

      So then I went and altered the lizardURL from:

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

      类似于:

      to something like: https://skdhfsupload.qwiklkjlkjimedia.com/qwikipehkjdia/eeeeeecommons/sdalfjkdse/aldskfjae0/extraParam/anotherextraparam/asasdLarge_Scaled_Forest_Lizard.jpeg

      我在 URL 中添加了愚蠢的字符.我还在其中添加了额外的片段.我最后更改了文件类型.

      I added dumb characters in the URL. I also added extra segments into it. I changed the file type at the end.

      它仍然有效.所以我唯一能得出的结论是,来自 Headers 的某些东西正在做决策.

      Still it was working. So the only thing I can conclude is that something from the Headers is doing the decision making.

      我的 landscapeURL 的标题是:(缓存另一个 URL 对此不起作用)

      The headers for my landscapeURL are: (caching for another URL doesn't work for this)

      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
      

      我的BearURL 是:(缓存另一个 URL 对此有效)

      The headers for my BearURL are: (caching for another URL works for this)

      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)
      

      重要提示:

      对于 BearURL,缓存 BearURL lizardURL 或任何其他 URL 都有效.对于landscapeURL,缓存仅适用于landscapeURL 本身.它不适用于任何other URL.

      Important note:

      For the BearURL, caching for the BearURL and lizardURL or any other URL works. For landscapeURL, caching only works for the landscapeURL itself. It doesn't work for any other URL.

      推荐答案

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

      Welcome to the wonderful world of asynchronous caches. NSURLCache is highly asynchronous. Just because you've shoved data into it doesn't mean it is available for retrieval. You have to let the main run loop return before it will be available, and possibly even wait a little while. The failure to return a response immediately after storing it is not at all unusual. Try dispatching it after five seconds or so.

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

      Second, your cache might be a bit on the small size for storing multi-megabyte images. Try bumping that up and see if it helps.

      最后,您说关闭互联网"是什么意思?你说你要超时了.通常,如果您在禁用所有连接的情况下将设备置于飞行模式,则在出现指示无连接的错误之前,它不应在那里停留很长时间.如果这没有发生,就会发生一些奇怪的事情,就好像 waitsForConnectivity 正在会话中设置一样.(您不是在后台发出网络请求,是吗?如果是这样,请尝试将 waitsForConnectivity 显式设置为 NO,这样它们就不会等待连接可用.)

      Finally, what do you mean when you say that you "turn off your Internet?" You say that you're getting a timeout. Normally, if you put the device into Airplane mode with all connectivity disabled, it should not sit there for any significant amount of time before failing with an error indicating no connectivity). If that isn't happening, something strange is happening, almost as if waitsForConnectivity is getting set on the session or something. (You aren't making the network requests in the background, are you? If so, try explicitly setting waitsForConnectivity to NO so that they won't wait for a connection to be available.)

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

      Also, for this usage, you may have to strip out the Vary: Accept-Encoding header or provide a consistent user agent string. That header causes the cache to basically be per-browser. This may cause the cache to behave in unexpected ways, and is probably the cause of the weirdness you're seeing.

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

      Note that stripping out the Vary header is a bit of a hack, and probably isn't the most correct way to fix the issue; ideally, you should tweak whatever outgoing header fields you have to tweak so that it works even with that header present. But you'd have to research it and figure out exactly what fields are needed, because I have no idea. :-)

      这篇关于使用 storeCachedResponse 存储在缓存中后,不会检索 URLresponse的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆