[英]URLResponse cached although response's cache-control header is set to no-cache
[英]URLresponse is not retrieved after storing in cache using storeCachedResponse
我試圖將數據/響應從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])
}
}
landscapeURLString
具有一個cache-control
標頭, max-age
31536000
為31536000
cachedResponse
為lizardURLString是nil
。 但是存儲之后 ,它不再是nil
。 結果,我得出結論,我已成功將某些內容存儲到緩存中! lizardURLString
。 它與我存儲的密鑰相同。 結合以上幾點,我得出結論,確切的密鑰存在於緩存中! currentMemoryUsage
增加。 顯然,它是離線的。 我希望它可以從緩存中使用,但事實並非如此。 我得到的只是一個超時!
我還嘗試將cachePolicy
更改為returnCacheDataElseLoad
,但這也無濟於事
編輯1:
我還嘗試着做大衛說過的話:
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
放入緩存。 那也不起作用。 它也會超時-並非每次都進入緩存。
編輯2:
所以我取得了一些進展。 或者,也許退后了一步,又前進了一步。
我嘗試查看是否可以為相同的 URL存儲響應,並查看清空緩存后是否可以檢索響應。 我沒能力
我正在創建這樣的緩存響應:
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
屬性存儲到緩存中。 我不確定從緩存本身拉出和從Response + Data
創建緩存之間有什么區別。
這很重要,因為我開始質疑URLCache中是否仍然存在某種形式的內部錯誤 。 這使我有理由相信它可能會按預期工作。
現在,我知道將數據存儲到緩存的過程了。 我知道我的URLResponse很好。 我只需要通過映射URLRequest來工作
編輯3:
Guy Kogus建議我的URL必須來自同一來源。 因此,一旦我下載了他提到的bearImage,我的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
我在網址中添加了啞字符。 我還添加了額外的細分。 我在最后更改了文件類型。
它仍然在工作。 因此,我唯一可以得出的結論是,標頭中的某項內容正在做出決策。
我的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
我的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)
對於BearURL,可以緩存BearURL 和 lizardURL或任何其他URL。 對於landscapeURL,緩存僅適用於landscapeURL本身。 它不適用於任何其他 URL。
歡迎來到異步緩存的美好世界。 NSURLCache是高度異步的。 僅僅因為您將數據塞入其中並不意味着它可用於檢索。 您必須先讓主運行循環返回,然后才能使用它,甚至可能要稍等片刻。 在存儲響應后立即返回響應的失敗並非罕見。 大約五秒鍾后嘗試調度它。
其次,您的緩存可能有點小,無法存儲數兆字節的圖像。 嘗試將其增大,看看是否有幫助。
最后,當您說“關閉互聯網”是什么意思? 您說您超時了。 通常,如果您在禁用所有連接的情況下將設備置於“飛行”模式,則在出現故障(指示沒有連接)之前,設備不應坐在那里過多時間。 如果這沒有發生,則正在發生奇怪的事情,幾乎就像在會話上設置了waitsForConnectivity一樣。 (您不是在后台發出網絡請求,是嗎?如果是,請嘗試將waitsForConnectivity顯式設置為NO,這樣它們就不會等待連接可用。)
同樣,對於這種用法,您可能必須去除Vary:Accept-Encoding標頭或提供一致的用戶代理字符串。 該頭導致高速緩存基本上是每個瀏覽器。 這可能會導致緩存以意外的方式運行,並且可能是您所看到的怪異現象的原因。
請注意,剝離Vary標頭有點麻煩,並且可能不是解決此問題的最正確方法。 理想情況下,您應該調整必須調整的所有傳出標頭字段,以便即使存在該標頭也可以使用。 但是您必須對其進行研究,並弄清楚需要哪些領域,因為我不知道。 :-)
這不是一個完整的答案,但是它應該使您朝正確的方向發展。
問題與您的代碼無關,我相信這基本上沒問題。 問題在於您從landscapeURLString
得到的響應,因為圖像存儲在Cloudflare中。 如果您使用同一來源的2張圖片(例如,嘗試使用Wikipedia上的那只熊而不是images.pexels.com上的圖片),則該圖片應該可以使用。
我嘗試打印出下載images.pexels.com圖片的響應和標題,這就是我看到的內容:
response: <NSHTTPURLResponse: 0x600002bf65c0> { URL: https://upload.wikimedia.org/wikipedia/commons/e/e0/Large_Scaled_Forest_Lizard.jpg } { Status Code: 200, Headers {
"Accept-Ranges" = (
bytes
);
"Cache-Control" = (
"public, max-age=31536000"
);
"Content-Length" = (
997361
);
"Content-Type" = (
"image/jpeg"
);
Date = (
"Wed, 31 Oct 2018 11:38:52 GMT"
);
Expires = (
"Thu, 31 Oct 2019 11:38:52 GMT"
);
"Last-Modified" = (
"Fri, 26 Oct 2018 6:31:56 GMT"
);
Server = (
cloudflare
);
Vary = (
"Accept-Encoding"
);
"cf-cache-status" = (
HIT
);
"cf-ray" = (
"4725d67b0ae461bd-BCN"
);
"expect-ct" = (
"max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\""
);
"x-cache" = (
"HIT, MISS"
);
"x-content-type-options" = (
nosniff
);
"x-served-by" = (
"cache-lax8643-LAX, cache-mad9437-MAD"
);
} }
headers: ["Accept-Ranges": "bytes", "Content-Type": "image/jpeg", "Last-Modified": "Fri, 26 Oct 2018 6:31:56 GMT", "Vary": "Accept-Encoding", "cf-ray": "4725d67b0ae461bd-BCN", "Date": "Wed, 31 Oct 2018 11:38:52 GMT", "Server": "cloudflare", "Expires": "Thu, 31 Oct 2019 11:38:52 GMT", "x-content-type-options": "nosniff", "expect-ct": "max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"", "x-cache": "HIT, MISS", "x-served-by": "cache-lax8643-LAX, cache-mad9437-MAD", "cf-cache-status": "HIT", "Content-Length": "997361", "Cache-Control": "public, max-age=31536000"]
那里可能存在某種嘗試將請求URL與導致緩存未命中的響應字段進行匹配的方法,但是我對此卻知之甚少。 其他人可能會為您找到它(因此,為什么我說這個答案不完整)。
我通過用以下命令替換dataTaskcompleteHandler中的第一個警衛來解決此問題:
guard error == nil else {
print(error)
if let cr = session.configuration.urlCache?.cachedResponse(for: urlRequest){
let image = UIImage(data: cr.data)
DispatchQueue.main.async {
self?.imageView.image = image
}
}
return
}
如果請求失敗,它將對該請求采取緩存的響應
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.