C# WebClient禁用缓存

30

你好。

我在我的C#应用程序中使用WebClient类,每分钟下载同一个文件,然后应用程序执行简单的检查以查看文件是否已更改,如果确实有更改,则对其进行某些操作。

由于这个文件每分钟都会被下载,所以WebClient缓存系统会缓存该文件,而不是再次下载该文件,只是从缓存中获取,这妨碍了检查是否下载的文件是新文件。

因此,我想知道如何禁用WebClient类的缓存系统。

我尝试过。

Client.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.BypassCache);

我也尝试了标题。

WebClient.Headers.Add("Cache-Control", "no-cache");

没有起作用。那么我该如何彻底禁用缓存?

谢谢。

编辑

我还尝试了以下 CacheLevels: NoCacheNoStore, BypassCache, Reload,但没有效果。然而,如果我重新启动电脑,缓存似乎被清除了,但我不能每次都重新启动电脑。

最近的活动更新(2012年9月8日)

被标记为已解决的answer解决了我的问题。简单来说,我使用套接字下载文件解决了我的问题。基本上是对所需文件的GET请求,我不会详细解释如何做到这一点,因为我相信你可以在SO上找到很多“how to”来自己做同样的事情。虽然这并不意味着我的解决方案也适用于你,我的第一个建议是阅读其他答案,看看是否有任何有用的内容。

无论如何,既然这个问题最近有些活跃,我想添加这个更新,包括我认为应该考虑的一些提示或想法,这些提示或想法应该被那些尝试过所有他们能想到的方法,并且确定问题不在他们的代码中的人考虑。 对于大多数情况来说,可能是代码的问题,但有时我们只是没有看到它,走一走,几分钟后回来,你可能会像显而易见的事情一样看到它。

无论如何,如果你确定了,那么我建议检查一下你的请求是否通过具有缓存功能的其他设备(计算机、路由器、代理等)直到它到达预定目的地。请注意,大多数请求都经过之前提到的一些设备,更常见的是路由器,除非当然,你通过服务提供商网络直接连接到互联网。

曾经我的路由器缓存了文件,很奇怪,但事实就是这样,每当我重新启动它或直接连接到互联网时,我的缓存问题就消失了。而且没有其他设备连接到路由器可以受到指责,只有计算机和路由器。

顺便说一下,一个普遍的建议,尽管它大多适用于那些在公司开发计算机而不是自己的计算机上工作的人。你的开发计算机可能会运行某种缓存服务吗?这是可能的。

此外,请考虑许多高端网站或服务使用内容传送网络(CDN),并且根据CDN提供商的不同,每当文件更新或更改时,这些更改需要一些时间才能反映在整个网络中。因此,你可能在请求正在更新的文件时运气不好,并且最近的CDN服务器尚未完成更新。

无论如何,特别是如果你一遍又一遍地请求相同的文件,或者如果你找不到问题所在,那么如果可能的话,我建议你重新考虑一下自己的方法,不要一次又一次地请求相同的文件,而是寻求构建一个简单的Web Service来满足你最初想要用这个文件满足的需求。

如果你正在考虑这样的选项,我认为你可能会更容易地为自己的需求构建一个REST风格的Web API

希望这个更新对你有所帮助,对我来说肯定会有帮助。祝你编码愉快。


当使用no-cache时,您能否举例请求和响应头?根据您在这里迄今为止所说的,它似乎应该起作用。 - Jon Hanna
你是否在每个请求中使用相同的WebClient实例?如果您将其处理并每次创建一个新实例会发生什么? - rossisdead
@rossisdead:完全一样。 - Fábio Antunes
12个回答

32

每次下载文件时,你可以尝试将一些随机数作为查询字符串的一部分附加到你的URL中。这可以确保每个URL每次都是唯一的。

例如:

Random random = new Random();
string url = originalUrl + "?random=" + random.Next().ToString();
webclient.DownloadFile(url, downloadedfileurl);

@Vinary:很遗憾地说,即使在文件URL的末尾使用随机值作为参数,也无法下载文件而不使用文件的缓存版本。 - Fábio Antunes
1
这是一个对我非常有效的天才黑客技巧。然而,我已经修改了它,不再使用随机数,而是使用从Environment.TickCount开始递增的静态数字。干杯。 - swinefeaster
这绝对不是问题的正确答案,但对于我们所有试图找出如何防止WebBrowser控件中显示缓存版本页面的人来说肯定非常有帮助。对我很有效。 - Daniel
3
旧答案。但我应该指出,让随机数正确独特的最佳方法是使用DateTime.Now.Ticks - Saurabh3321
我正在使用类似的东西,在每个请求的末尾附加一个Guid.NewGuid()。确保使用?或&来追加参数(或者在我的情况下,只是使用#来创建一个虚假的书签)。 - ccalboni
该死 - 你们真是救命恩人! - stigzler

13

从上述信息推断,您的问题可能出在其他地方。请问您是否可以在服务器端记录HTTP请求?当您更改某个随机种子参数时,会得到什么结果?

如果日志显示每分钟确实触发了该请求,那么可能是服务器缓存了文件。

您使用ISA或者SQUID吗?

您的请求的HTTP响应代码是什么?

我知道直接回答问题可能不太受欢迎,但评论框不允许我输入这么多文字 :)

编辑:

无论如何,请使用HttpRequest对象代替WebClient,希望(如果您将疑虑放在WebClient上)一切都能得到解决。如果换成HttpRequest后问题仍未解决,则问题确实出在其他地方。

进一步优化:

甚至可以更低层: 如何在.Net中手动创建HTTP请求?

这是纯套接字操作,如果问题仍然存在,请发布一个新的问题,并打上WTF标签 :)


@Daniel:关于服务器,我一无所知,因为它不是我的,也不受我控制。然而,文件被System.Net和IE缓存,但在使用Firefox时不会被缓存。我知道这是因为有时我会在我的应用程序中中断zip下载,导致下载的zip文件损坏,有时这样的损坏的zip文件会留在缓存中,每次我的应用程序或IE再次下载该文件时,它都会从缓存中下载损坏的版本。但如果我使用Firefox下载该文件,他会完美地下载zip文件。 - Fábio Antunes
好的,但是其他想法呢?你是否有涉及到代理服务器之类的东西? - Daniel Mošmondor
@Daniel:没有使用代理。我还尝试通过IE清除缓存,但手动清除也不起作用。我已经尝试使用WebClient下载文件,并使用WebRequest,然后将ResponseStream保存到文件中,但是他们都使用缓存,即使使用随机参数技术和设置每个请求的CachePolicy属性不使用缓存。而且Cache-Control头也没有起作用。 - Fábio Antunes
1
@Daniel:我怀疑这不会起作用,因为HttpWebResquest和我使用的其他类共享相同的基础类。基本上它们几乎是一样的。 - Fábio Antunes
@Daniel。一个套接字请求!我有一种感觉这可能会起作用,你应该在回答中发布它,而我尝试它。这样,有相同问题的人可以轻松找到答案。 - Fábio Antunes
显示剩余2条评论

10

尝试使用NoCacheNoStore

从不使用缓存资源满足请求并且不缓存资源。如果资源存在于本地缓存中,则会将其删除。该策略级别向中间缓存表明它们应删除该资源。在HTTP缓存协议中,这是通过使用no-cache缓存控制指令实现的。

client.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore); 

@Fabio,也许你需要使用Webrequest来完成这个任务。 - Thakur
BranchCache服务(Windows 7)可能仍然存在冲突 - 关闭它,进行测试。 - kagali-san
@mhambra:BranchService是基于本地网络资源的缓存服务,与Web资源无关,但它已经关闭了,仍然不能工作。 - Fábio Antunes
@Fábio Antunes:你能和我们分享一下你的确切代码,可能是整个方法以及你调用方法的方式吗? - KMån

7
在某些情况下,网络调试软件可能会导致此问题。为了确保您的URL没有被缓存,您可以添加一个随机数作为最后一个参数,使URL成为唯一的。这个随机参数在大多数情况下被服务器忽略(服务器试图读取作为名称值对发送的参数)。
例如: http://www.someserver.com/?param1=val1&ThisIsRandom=RandomValue 其中ThisIsRandom=RandomValue是新添加的参数。

我知道你的意思,如果我想下载一个网页,那么这个方法是可行的。但在这种情况下,我要下载的是一个文件,其URL类似于:www.example.com/files/now.zip。 - Fábio Antunes
这应该仍然可以工作 - 即使使用除html之外的请求类型。URL看起来像www.example.com/files/now.zip?rand=randomvalue - Dan Esparza
就像丹所说的那样,它应该适用于所有类型的请求。 - ivymike
@Dan Esparza、Ivymike:你们说得对,它起作用了。从未想过在文件URL末尾使用参数。谢谢。 - Fábio Antunes
@Dan Esparza,Ivymike:遗憾的是,即使在文件URL末尾设置一个随机值作为参数也无法解决此问题。 - Fábio Antunes

2
client.CachePolicy = new RequestCachePolicy(RequestCacheLevel.BypassCache);

应该可以运行。在运行代码之前,请确保清除Internet Explorer中的缓存并删除任何临时下载文件,因为System.Net和IE都使用相同的缓存。


仍然不起作用。我尝试了每个CacheLevel以禁用文件缓存,但没有任何效果,清理IE临时文件也没有任何影响。 - Fábio Antunes

2

我使用webClient时遇到了类似的问题,转而使用webRequest后问题仍然存在。我发现套接字被重用导致了各种服务器/网络端缓存(在我的情况下,负载均衡器也成为了一个特别麻烦的问题,尤其是https)。解决此问题的方法是在webrequest对象中禁用keepalive和可能的pipeling,如下所示,这将强制为每个请求使用新的套接字:

#Define Funcs Function httpRequest {
     param([string]$myurl)
     $r = [System.Net.WebRequest]::Create($myurl)
     $r.keepalive = 0
     $sr = new-object System.IO.StreamReader (($r.GetResponse()).GetResponseStream())
     $sr.ReadToEnd() }

1

我猜你需要使用webrequest/webresponse而不是webclient

    WebRequest request = WebRequest.Create(uri);
     // Define a cache policy for this request only. 
     HttpRequestCachePolicy noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
     request.CachePolicy = noCachePolicy;
     WebResponse response = request.GetResponse();

//below is the function for downloading the file

   public static int DownloadFile(String remoteFilename,
                           String localFilename)
    {
        // Function will return the number of bytes processed
        // to the caller. Initialize to 0 here.
        int bytesProcessed = 0;

        // Assign values to these objects here so that they can
        // be referenced in the finally block
        Stream remoteStream = null;
        Stream localStream = null;
        WebResponse response = null;

        // Use a try/catch/finally block as both the WebRequest and Stream
        // classes throw exceptions upon error
        try
        {
            // Create a request for the specified remote file name
            WebRequest request = WebRequest.Create(remoteFilename);
            // Define a cache policy for this request only. 
            HttpRequestCachePolicy noCachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);
            request.CachePolicy = noCachePolicy;
            if (request != null)
            {
                // Send the request to the server and retrieve the
                // WebResponse object 
                response = request.GetResponse();

                if (response != null)
                {
                    if (response.IsFromCache)
                        //do what you want

                    // Once the WebResponse object has been retrieved,
                    // get the stream object associated with the response's data
                    remoteStream = response.GetResponseStream();

                    // Create the local file
                    localStream = File.Create(localFilename);

                    // Allocate a 1k buffer
                    byte[] buffer = new byte[1024];
                    int bytesRead;

                    // Simple do/while loop to read from stream until
                    // no bytes are returned
                    do
                    {
                        // Read data (up to 1k) from the stream
                        bytesRead = remoteStream.Read(buffer, 0, buffer.Length);

                        // Write the data to the local file
                        localStream.Write(buffer, 0, bytesRead);

                        // Increment total bytes processed
                        bytesProcessed += bytesRead;
                    } while (bytesRead > 0);
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
        finally
        {
            // Close the response and streams objects here 
            // to make sure they're closed even if an exception
            // is thrown at some point
            if (response != null) response.Close();
            if (remoteStream != null) remoteStream.Close();
            if (localStream != null) localStream.Close();
        }

        // Return total bytes processed to caller.
        return bytesProcessed;
    }

是的,我尝试过这种方法,但是我无法将响应保存到文件中。而这本来就是最初的目的。 - Fábio Antunes
1
你是否曾经使用过WebRequest类下载文件?那么你如何准确地保存这个文件呢? - Fábio Antunes
你试过这段代码吗? 在我发这个问题之前,我尝试了这种方法,但它没有起作用... 在bytesRead = remoteStream.Read(buffer, 0, buffer.Length)处出现NullReferenceException。 我还尝试将其替换为“int bytesRead = 0;”,但仍然存在同样的问题。 - Fábio Antunes
我已经成功修复了代码以将请求保存为流,但是 WebRequest 仍然使用缓存来加载文件。 - Fábio Antunes

1
使用 HTTPRequest 绝对是解决您问题的正确答案。但是,如果您希望防止 WebBrowser / WebClient 对象使用缓存页面,您应该包括不仅是 "no-cache",而且这些头文件:
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache-control" content="no-store">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="-1">

在IE11中,直到我包括最后两个中的一个或两个才能正常工作。

1
所有方法似乎都不能解决一个问题: 如果网页曾经是可访问的,现在从服务器删除了,方法HttpWebResponse.GetResponse()将为您提供一个响应,该响应开始于缓存副本,直到“足够时间段过去之前或重新启动计算机,它不会触发预期的404页面未找到错误异常,您无法确定现在该网页是否已完全不存在。”
我尝试了一切:
- 设置标题如(“Cache-Control”,“no-cache”) - 将“request.CachePolicy”设置为“noCachePolicy” - 删除IE临时/历史文件。 - 使用有线互联网而不是路由器……不起作用!
幸运的是,如果网页更改了其内容,HttpWebResponse.GetResponse()将为您提供一个新的页面以反映更改。

0

请检查您是否被限制速率!我从nginx服务器收到以下内容:

403禁止访问

超过速率限制,请在24小时后重试。

这是我使用的程序(C#)

using System;
using System.IO;
using System.Net;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            DownloadFile();
            Console.ReadLine();
        }

        public static void DownloadFile()
        {
            var downloadedDatabaseFile = Path.Combine(Path.GetTempPath(), Path.GetTempFileName());
            Console.WriteLine(downloadedDatabaseFile);

            var client = new WebClient();
            client.DownloadProgressChanged += (sender, args) =>
            {
                Console.WriteLine("{0} of {1} {2}%", args.BytesReceived, args.TotalBytesToReceive, args.ProgressPercentage);
            };

            client.DownloadFileCompleted += (sender, args) =>
            {
                Console.WriteLine("Download file complete");

                if (args.Error != null)
                {
                    Console.WriteLine(args.Error.Message);
                }
            };

            client.DownloadFileAsync(new Uri("http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dats.gz"), downloadedDatabaseFile);
        }
    }
}

控制台打印输出:

C:\Users\jake.scott.WIN-J8OUFV09HQ8\AppData\Local\Temp\2\tmp7CA.tmp
Download file complete
The remote server returned an error: (403) Forbidden.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接