问题描述
我正在尝试将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-age
为31536000
- 如果是全新安装,则之前存储到缓存中,我的lizardURLString的
cachedResponse
是nil
.但是 存储后,它不再是nil
.结果,我得出结论,我已经成功地将某些东西存储到缓存中了! - 我还怀疑URLCache将URLRequest视为键.所以我打印了
lizardURLString
的hashValue.它与我存储的密钥相同.结合以上几点,我得出结论,确切的密钥存在于缓存中! - 我还可以看到,将其存储在缓存中时,
currentMemoryUsage
会增加. - My
landscapeURLString
has acache-control
header with amax-age
of31536000
- If it's a fresh install, then before storing into the cache, my
cachedResponse
for lizardURLString isnil
. But after storing, it's no longernil
. 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. - 我只是下载风景图片.
- 关闭我的互联网
- 单击按钮以下载蜥蜴图像.
Things I've already validated:
很明显,它处于离线状态.我希望它可以从缓存中使用,但事实并非如此.我得到的只是一个超时!
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
我也尝试着做大卫说过的话:
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
然后我:
- 清除了缓存
- 关闭互联网
- 试图下载图像,但没有成功,这是好的.这是预期的行为
- 将
cachedResponseFromCache
属性存储到缓存中. - 能够从缓存中检索!
- flushed the cache
- turned off internet
- attempted to download image, but had no success which is good. It's the expected behavior
- stored
cachedResponseFromCache
property into the cache. - was able to retrieve from cache!
我不确定从缓存本身中退出与从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
我在网址中添加了哑字符.我还添加了额外的细分.我在最后更改了文件类型.
I added dumb characters in the URL. I also added extra segments into it. I changed the file type at the end.
仍然有效.因此,我唯一可以得出的结论是,标头中的某项内容正在做出决策.
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是高度异步的.仅仅因为您将数据塞入其中并不意味着它可用于检索.您必须先让主运行循环返回,然后才能使用它,甚至可能要稍等片刻.在存储响应后立即返回响应的失败并非罕见.尝试在大约五秒钟后分派它.
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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!