我想實現以下功能:如何僅在圖片較新時從HTTP下載圖片?
一個C#客戶端連接到一個HTTP服務器並下載到磁盤的圖像。
客戶端下一次開始檢查服務器上的映像是否比磁盤上的映像更新,並且在這種情況下,客戶端將覆蓋磁盤上的映像。
對我來說,很容易下載圖像,但我不知道如何檢查服務器上的圖像是否更新。我怎麼能實現它?我想我可以檢查時間戳,或圖像大小(或兩者),但我不知道如何去做。
我想實現以下功能:如何僅在圖片較新時從HTTP下載圖片?
一個C#客戶端連接到一個HTTP服務器並下載到磁盤的圖像。
客戶端下一次開始檢查服務器上的映像是否比磁盤上的映像更新,並且在這種情況下,客戶端將覆蓋磁盤上的映像。
對我來說,很容易下載圖像,但我不知道如何檢查服務器上的圖像是否更新。我怎麼能實現它?我想我可以檢查時間戳,或圖像大小(或兩者),但我不知道如何去做。
的HttpWebRequest可以只使用IE緩存,因此,如果所有的圖像會在緩存反正,並重新寫入文件(但不必下載)的成本是可以接受的,你可以做使用。
如果您需要自己雖然處理一下,然後:
考慮:
string uri; //URI of the image.
DateTime? lastMod; // lastModification date of image previously recorded. Null if not known yet.
string eTag; //eTag of image previously recorded. Null if not known yet.
你必須要存儲這些在本月底,並再次檢索它們(如果不是新圖像)在開始。這是給你的,因爲,其餘工作:
var req = (HttpWebRequest)WebRequest.Create(uri);
if(lastMod.HasValue)
req.IfModifiedSince = lastMod.Value;//note: must be UTC, use lastMod.Value.ToUniversalTime() if you store it somewhere that converts to localtime, like SQLServer does.
if(eTag != null)
req.AddHeader("If-None-Match", eTag);
try
{
using(var rsp = (HttpWebResponse)req.GetResponse())
{
lastMod = rsp.LastModified;
if(lastMod.Year == 1)//wasn't sent. We're just going to have to download the whole thing next time to be sure.
lastMod = null;
eTag = rsp.GetResponseHeader("ETag");//will be null if absent.
using(var stm = rsp.GetResponseStream())
{
//your code to save the stream here.
}
}
}
catch(WebException we)
{
var hrsp = we.Response as HttpWebResponse;
if(hrsp != null && hrsp.StatusCode == HttpStatusCode.NotModified)
{
//unfortunately, 304 when dealt with directly (rather than letting
//the IE cache be used automatically), is treated as an error. Which is a bit of
//a nuisance, but manageable. Note that if we weren't doing this manually,
//304s would be disguised to look like 200s to our code.
//update these, because possibly only one of them was the same.
lastMod = hrsp.LastModified;
if(lastMod.Year == 1)//wasn't sent.
lastMod = null;
eTag = hrsp.GetResponseHeader("ETag");//will be null if absent.
}
else //some other exception happened!
throw; //or other handling of your choosing
}
E-標籤比更可靠的最後一次修改實施時正確(注意關於修改亞秒決議,反映了由於不同的接受 - 不同反應*標題)。一些實現是buggy,雖然(沒有特別調整的web農場上的IIS6,使用mod-gzip的Apache),因此值得拿出與電子標籤相關的代碼並且僅僅依靠日期。
編輯:如果你想進一步實現HTTP緩存,你也可以存儲expires和max-age(如果它們都存在並且不同意前者,則使用後者)並且完全跳過下載它的價值早於這些價值。我已經做到了這一點,它運行良好(我有一個從各種URI返回的XML創建的對象的內存緩存,如果XML是新鮮的或沒有改變,我重新使用了該對象),但它可能與你的需求無關(如果你關心的是比服務器更新鮮,或者你總是在窗口外面)。
嘗試If-Modified-Since
請求字段。 http://en.wikipedia.org/wiki/List_of_HTTP_header_fields 我不確定它是否完全支持每個服務器。所以如果它不被支持,你仍然會得到這個文件(而不是304,如果它被支持),你可以計算校驗和,如果它們不同,考慮修改文件。或者只是覆蓋 - 而且您將始終擁有最新版本。
不支持它的服務器很少。你有時會發現它不支持每種資源(例如,ASP.NET或PHP頁面默認情況下不支持,因爲它需要知道發送日期的邏輯,雖然它們當然可以支持它),但所有服務器至少支持去「對不起,我不知道,這是整件事情」而不是窒息。我給的代碼使用了這個,還有電子標籤。 – 2012-08-07 17:08:53
您需要閱讀RFC 2616和相關的RFC(在http://www.rfc-editor.org/cgi-bin/rfcsearch.pl上搜索1616)。具體而言,§ 13是感興趣的,在HTTP中緩存,第47頁– 62.然後閱讀相關的請求/響應頭和相關的狀態代碼,您可能會收到回覆。
您可以通過HttpWebRequest
和HttpWebResponse
類訪問所有標題和狀態值。
但應該注意的是,您可以向服務器詢問您想要的任何內容:最終,它是決定是否向您發送該URI的新表示的服務器。您可能需要使用HTTP HEAD
動詞而不是其動詞GET
來詢問服務器關於資源的信息。
HEAD方法與GET相同,但服務器不得在響應中返回 消息體。響應於HEAD請求,包含在HTTP報頭 中的元信息應當與在響應GET請求的 中發送的信息相同。此方法可用於獲取有關請求所隱含的實體的元信息 ,而無需傳輸實體主體本身。 此方法通常用於測試超文本鏈接的有效性,可訪問性, 和最近的修改。
發送HEAD有什麼用?要麼發送一個304,在這種情況下它與GET相同,或者它將發送一個200,在這種情況下,唯一確定的方法是在它之後發出一個GET。 – 2012-08-07 17:13:02
HEAD不會返回物體; GET可能會或可能不會。 HEAD將讓您無需檢索新的表示即可使緩存的項目失效。根據需求,推遲向緩存添加新的表示可能是首選行爲。因此,我的答案中有條件的'**可能**'。 – 2012-08-07 19:26:20
但是在HEAD返回200的情況下,我們可能知道我們一定要GET(不同的內容長度),但不是我們絕對不會(如果它們具有相同的內容長度,它們仍可能不同圖像),所以我們必須要GET。唯一的可能是如果有人發送MD5頭文件,而不發送最後一個mod或電子標籤,這在技術上是可行的,但是這樣的人應該得到一個耳光,而且在現實生活中不太可能(MD5頭文件很少,最後-mod和電子標籤是幾乎所有服務器上的「靜態文件」的默認行爲,有時甚至超過這些)。 – 2012-08-07 19:33:29
您可能會感興趣:https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching – rboarman 2012-08-07 16:38:42
您是否可以控制Web服務器?如果沒有,它是否支持緩存,Etags,...提供這些圖像時? – 2012-08-07 16:38:48
不知道這將如何通過HTTP工作,但您可以嘗試[FileInfo的CreationTime屬性](http://msdn.microsoft.com/zh-cn/library/system.io.filesysteminfo.creationtime.aspx)。我不確定的原因是我無法確定這個屬性的值是否是實際的創建日期時間,或者它是否是您上次訪問它的日期(我對這個主題有點無知)。值得看看。 – MilkyWayJoe 2012-08-07 16:40:13