关于利用浏览器缓存有几个问题,但我没有找到任何有助于在ASP.NET应用程序中执行此操作的内容。谷歌的Pagespeed告诉这是性能最大的问题。 到目前为止,我在 web.config 中执行了此操作:
<system.webServer>
<staticContent>
<!--<clientCache cacheControlMode="UseExpires"
httpExpires="Fri, 24 Jan 2014 03:14:07 GMT" /> -->
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.24:00:00" />
</staticContent>
</system.webServer>
评论代码有效。我可以将expire header设置为将来某个特定时间但我无法设置cacheControlMaxAge
来设置从现在开始静态内容的缓存天数。这是行不通的。我的问题是:
我该怎么做? 我知道有可能只为特定文件夹设置缓存,这是一个很好的解决方案,但它也不起作用。应用程序托管在Windows Server 2012上,在IIS8上,应用程序池设置为经典。
在web配置中设置此代码后,我的页面速度为72(之前为71)。没有缓存50个文件。 (现在49)我想知道为什么,我只是意识到一个文件实际上是缓存的(svg文件)。不幸的是png和jpg文件没有。 这是我的 web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="exceptionManagement" type="Microsoft.ApplicationBlocks.ExceptionManagement.ExceptionManagerSectionHandler,Microsoft.ApplicationBlocks.ExceptionManagement" />
<section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E34" requirePermission="false" allowDefinition="Everywhere" />
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
</sectionGroup>
</configSections>
<exceptionManagement mode="off">
<publisher mode="off" assembly="Exception" type="blabla.ExceptionHandler.ExceptionDBPublisher" connString="server=188......;database=blabla;uid=blabla;pwd=blabla; " />
</exceptionManagement>
<location path="." inheritInChildApplications="false">
<system.web>
<httpHandlers>
<add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler,System.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e34" validate="false" />
<add verb="GET" path="Image.ashx" type="blabla.WebComponents.ImageHandler, blabla/>"
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
<add verb="*" path="*.jpg" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.js" type="System.Web.StaticFileHandler" />
<add verb="*" path="*.gif" type="System.Web.StaticFileHandler" />
<add verb="GET" path="*.css" type="System.Web.StaticFileHandler" />
</httpHandlers>
<compilation defaultLanguage="c#" targetFramework="4.5.1" />
<trace enabled="false" requestLimit="100" pageOutput="true" traceMode="SortByTime" localOnly="true"/>
<authentication mode="Forms">
<forms loginUrl="~/user/login.aspx">
<credentials passwordFormat="Clear">
<user name="blabla" password="blabla" />
</credentials>
</forms>
</authentication>
<authorization>
<allow users="*" />
</authorization>
<sessionState mode="InProc" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" cookieless="false" timeout="20" />
<globalization requestEncoding="utf-8" responseEncoding="utf-8" culture="en-GB" uiCulture="en-GB" />
<xhtmlConformance mode="Transitional" />
<pages controlRenderingCompatibilityVersion="4.5" clientIDMode="AutoID">
<namespaces>
</namespaces>
<controls>
<add assembly="Microsoft.AspNet.Web.Optimization.WebForms" namespace="Microsoft.AspNet.Web.Optimization.WebForms" tagPrefix="webopt" />
</controls>
</pages>
<webServices>
<protocols>
<add name="HttpGet" />
<add name="HttpPost" />
</protocols>
</webServices>
</system.web>
</location>
<appSettings>
</appSettings>
<connectionStrings>
</connectionStrings>
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="200000" />
</webServices>
</scripting>
</system.web.extensions>
<startup>
<supportedRuntime version="v2.0.50727" />
<supportedRuntime version="v1.1.4122" />
<supportedRuntime version="v1.0.3705" />
</startup>
<system.webServer>
<rewrite>
<providers>
<provider name="ReplacingProvider" type="ReplacingProvider, ReplacingProvider, Version=1.0.0.0, Culture=neutral, PublicKeyToken=5ab632b1f332b247">
<settings>
<add key="OldChar" value="_" />
<add key="NewChar" value="-" />
</settings>
</provider>
<provider name="FileMap" type="DbProvider, Microsoft.Web.Iis.Rewrite.Providers, Version=7.1.761.0, Culture=neutral, PublicKeyToken=0525b0627da60a5e">
<settings>
<add key="ConnectionString" value="server=;database=blabla;uid=blabla;pwd=blabla;App=blabla"/>
<add key="StoredProcedure" value="Search.GetRewriteUrl"/>
<add key="CacheMinutesInterval" value="0"/>
</settings>
</provider>
</providers>
<rewriteMaps configSource="rewritemaps.config" />
<rules configSource="rewriterules.config" />
</rewrite>
<modules>
<remove name="ScriptModule" />
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3456AD264E35" />
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
</modules>
<handlers>
<add name="Web-JPG" path="*.jpg" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
<add name="Web-CSS" path="*.css" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
<add name="Web-GIF" path="*.gif" verb="GET,HEAD,POST" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
<add name="Web-JS" path="*.js" verb="GET,HEAD,POST,DEBUG" modules="IsapiModule" scriptProcessor="C:\Windows\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" resourceType="Unspecified" preCondition="classicMode,runtimeVersionv4.0,bitness64" />
</handlers>
<validation validateIntegratedModeConfiguration="false" />
<httpErrors errorMode="DetailedLocalOnly" existingResponse="Auto">
<remove statusCode="404" subStatusCode="-1"/>
<remove statusCode="500" subStatusCode="-1"/>
<error statusCode="404" path="error404.htm" responseMode="File"/>
<error statusCode="500" path="error.htm" responseMode="File"/>
</httpErrors>
</system.webServer>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="soapBinding_AdriagateService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true" messageEncoding="Text">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding name="NetTcpBinding_ITravellerService" closeTimeout="00:10:00" openTimeout="00:10:00" sendTimeout="00:10:00" maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="blabla" bindingConfiguration="soapBinding_blabla" contract="" Address="blabla" name="blabla" />
<endpoint address="blabla" binding="basicHttpBinding" bindingConfiguration="soapBinding_IImagesService"
contract="ImagesService.IImagesService" name="soapBinding_IImagesService"/>
<identity>
<servicePrincipalName value="blabla"/>
</identity>
</endpoint>
</client>
</system.serviceModel>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.5.0.0" newVersion="4.5.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.web>
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
</httpModules>
</system.web>
<elmah>
<security allowRemoteAccess="false" />
</elmah>
<location path="elmah.axd" inheritInChildApplications="false">
<system.web>
<httpHandlers>
<add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
</system.web>
<system.webServer>
<handlers>
<add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
</handlers>
</system.webServer>
</location>
</configuration>
修改 如果我设置了确切的到期日期,缓存正在运行,但不适用于jpg,gif ....仅适用于png
EDIT2:
如果我在这里设置cacheControlCustom="public"
:
<clientCache cacheControlCustom="public"
cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
缓存正在运行,但仍然不适用于jpegs和gif;它只适用于svgs和pngs。
答案 0 :(得分:102)
大多数浏览器缓存问题都可以通过查看响应标头来解决(可以在Google Chrome开发人员工具中完成)。
现在,clientCache
文件的web.config
部分应将输出缓存设置为最大年龄,如下图所示,已将max-age
设置为86400
是一天几秒钟。
以下是此设置的web.config代码段。
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00" />
现在很好,响应标头在max-age
标头上设置了Cache-Control
属性。所以浏览器应该缓存内容。嗯,这几乎是正确的,但有些浏览器需要设置另一个标志。特别是为缓存控制头设置的public
标志。可以使用cacheControlCustom
中的web.config
属性轻松添加此内容。这是一个例子。
<clientCache cacheControlCustom="public" cacheControlMode="UseMaxAge" cacheControlMaxAge="1.00:00:00" />
现在我们重试页面并检查标题。
现在您可以从上图中看到,我们现在拥有值public, max-age=86400
。所以我们的浏览器拥有缓存资源所需的一切。现在检查谷歌浏览器的标题和网络标签将有助于我们。
这是对文件的第一个请求..注意文件没有缓存...
现在让我们导航回这个页面(注意:不刷新页面,我们将在一秒钟内讨论)。您将看到现在从缓存返回的响应(如带圆圈所示)。
如果我使用 F5 或使用浏览器刷新功能刷新页面会发生什么。等等.. (from cache)
去了哪里。
谷歌Chrome浏览器(不确定其他浏览器)使用刷新按钮会重新下载静态资源,无论缓存标头如何(请在此处插入说明)。这意味着已重新检索资源并发送最大年龄标头。
现在完成上述所有解释后,请务必测试 以监控缓存标头。
<强>更新强>
根据您的评论,您声明自己拥有一个名为IHttpHandler
的通用处理程序(Image.ashx
),其内容类型为image/jpg
。现在您可能希望默认行为是缓存此处理程序。但是,IIS将扩展.ashx
(正确)视为动态脚本,并且在没有在代码本身中显式设置缓存头的情况下不受缓存限制。
现在这是您需要小心的地方,因为通常IHttpHandlers
通常不应该缓存,因为它们通常会提供动态内容。现在,如果该内容不太可能发生变化,您可以直接在代码中设置缓存标头。以下是使用IHttpHandlers
上下文在Response
中设置缓存标头的示例。
context.Response.ContentType = "image/jpg";
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1));
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetSlidingExpiration(true);
context.Response.TransmitFile(context.Server.MapPath("~/out.jpg"));
现在查看代码,我们在Cache
属性上设置了一些属性。为了获得所需的响应,我设置了属性。
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1));
告诉out put缓存将max-age=
标题的Cache-Control
部分设置为将来{8}晚的1
天。context.Response.Cache.SetCacheability(HttpCacheability.Public);
告诉out put缓存将Cache-Control
标头设置为public
。这非常重要,因为它告诉浏览器缓存到对象。context.Response.Cache.SetSlidingExpiration(true);
告诉输出缓存,以确保它正确设置max-age=
标头的Cache-Control
部分。如果不设置滑动过期,IIS输出缓存将忽略最大年龄标头。把它放在一起就可以得到这个结果。
如上所述,您可能不希望缓存.ashx
文件,因为它们通常会提供动态内容。但是,如果该动态内容在给定时间段内不太可能发生变化,您可以使用上述方法提供.ashx
文件。
现在结合上面列出的流程,您还可以设置缓存标头的 ETag (请参阅wiki)组件,以便浏览器可以验证自定义字符串传递的内容。维基说:
ETag是由Web服务器分配给特定的不透明标识符 在URL处找到的资源的版本。如果资源内容在那 网址会发生变化,会分配一个新的不同
ETag
。
因此,这实际上是浏览器的某种唯一标识,用于标识响应中传递的内容。通过提供此标头,下次重新加载的浏览器将通过If-None-Match
标头从最后一个响应发送ETag
。我们可以修改我们的处理程序以检测If-None-Match
标头并将其与我们自己生成的Etag
进行比较。现在没有确切的科学来生成ETags
,但一个好的经验法则是提供一个最有可能只定义一个实体的标识符。在这种情况下,我喜欢使用连接在一起的两个字符串,例如。
System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/saveNew.png"));
string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString();
在上面的代码片段中,我们从文件系统加载一个文件(您可以从任何地方获取此文件)。然后我使用GetHashCode()
方法(在所有对象上)来获取对象的整数哈希码。在示例中,我将文件名的哈希值连接起来,然后是最后一次写入日期。上次写入日期的原因是如果文件被更改,哈希码也会被更改,从而使指纹不同。
这将生成类似于306894467-210133036
的哈希码。
那么我们如何在我们的处理程序中使用它。下面是处理程序的新修改版本。
System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/out.png"));
string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString();
var browserETag = context.Request.Headers["If-None-Match"];
context.Response.ClearHeaders();
if(browserETag == eTag)
{
context.Response.Status = "304 Not Modified";
context.Response.End();
return;
}
context.Response.ContentType = "image/jpg";
context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1));
context.Response.Cache.SetCacheability(HttpCacheability.Public);
context.Response.Cache.SetSlidingExpiration(true);
context.Response.Cache.SetETag(eTag);
context.Response.TransmitFile(file.FullName);
正如您所看到的,我已经更改了很多处理程序但是您会注意到我们生成了Etag
哈希,检查传入的If-None-Match
标头。如果etag hash和header相等,那么我们通过返回状态代码304 Not Modified
告诉浏览器内容没有改变。
接下来是相同的处理程序,除了我们通过调用
添加ETag
标头
context.Response.Cache.SetETag(eTag);
当我们在浏览器中运行时,我们得到了。
您将从图像中看到(因为我确实更改了文件名),我们现在已经拥有了缓存系统的所有组件。 ETag
作为标头传递,浏览器正在发送请求标头If-None-Match
,因此我们的处理程序可以相应地响应缓存文件的更改。
答案 1 :(得分:5)
使用它。这对我有用。
function removeDuplicates() {
var sheet = SpreadsheetApp.getActive().getSheetByName('check-in');
var data = sheet.getDataRange().getValues();
var newData = new Array();
for(i in data){
var row = data[i];
var duplicate = false;
for(j in newData){
if(row.join() == newData[j].join()){
duplicate = true;
}
}
if(!duplicate){
newData.push(row);
}
}
sheet.clearContents();
sheet.getRange(1, 1, newData.length, newData[0].length).setValues(newData);
}
答案 2 :(得分:0)
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="10.00:00:00" />
</staticContent>
</system.webServer>
</configuration>
使用上述内容,静态内容文件将在浏览器中缓存10天。有关<clientCache>
元素的详细信息,请参见here。
您还可以使用<location>
元素定义特定文件的缓存设置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<location path="path/to/file">
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="10.00:00:00" />
</staticContent>
</system.webServer>
</location>
</configuration>