TinyMCE、gzip 和缓存
TinyMCE, gzip and caching
我正在尝试让 TinyMCE、gzip 和缓存正常工作,但浏览器无法缓存对 gzip.ashx 处理程序的请求。
我的设置:
- TinyMCE(当前为 4.1.7)
- TinyMCE Compressor (.NET 4.0.1)
- ASP.NET WebForm 应用程序/IIS。
这是我的代码(非常标准):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="/scripts/tinymce/tinymce.gzip.js"></script>
</head>
<body>
<script>
tinymce.init({
selector: 'textarea',
plugins: 'image link'
});
</script>
<textarea />
</body>
</html>
首次加载页面:
tinymce.gzip.js
请求 TinyMCE Compressor tinymce.gzip.ashx
tinymce.gzip.ashx
压缩所有 TinyMCE javascript 文件并生成一个压缩文件 (.gz),如 tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz
页面的后续加载:
tinymce.gzip.ashx
检查磁盘上是否存在 .gz 文件并将其 return 发送到浏览器
我的 tinymce.gzip.ashx(完整脚本在 post 末尾)看起来像原来的但有这个小改动,因为页面 diskcache
参数没有传入查询字符串:
..
...
themes = GetParam("themes", "").Split(',');
diskCache = true; //GetParam("diskcache", "") == "true";
isJS = GetParam("js", "") == "true";
..
无论如何,这一切工作正常,但真正的问题出现在浏览器缓存该 .gz 文件中。我永远无法得到 return 的 HTTP/1.1 304 Not Modified
响应,因此不会再次请求 .gz。所有其他文件都是 304'。
这是我尝试过的:
我已经尝试像 http://mysite/scripts/tinymce/tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz 一样直接请求 gzip,但我仍然得到 200 OK
手动设置Response.StatusCode = 304;
只会导致响应为空,不会加载tinymce。
执行 <script src="/scripts/tinymce/tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz"></script>
将 return .gz 文件但不会加载 TinyMCE
我现在已经在这上面花了五个小时 - 任何帮助都将不胜感激。
以下是 IE 11.0.9600.17498、FF 35.01 和 Fiddler 的一些屏幕截图:
完整的 tinymce.gzip.ashx 处理程序:
<%@ WebHandler Language="C#" Class="Handler" %>
/**
* tinymce.gzip.ashx
*
* Copyright, Moxiecode Systems AB
* Released under LGPL License.
*
* License: http://tinymce.moxiecode.com/license
* Contributing: http://tinymce.moxiecode.com/contributing
*
* This file compresses the TinyMCE JavaScript using GZip and
* enables the browser to do two requests instead of one for each .js file.
*
* It's a good idea to use the diskcache option since it reduces the servers workload.
*/
using System;
using System.Web;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
public class Handler : IHttpHandler {
private HttpResponse Response;
private HttpRequest Request;
private HttpServerUtility Server;
public void ProcessRequest(HttpContext context) {
this.Response = context.Response;
this.Request = context.Request;
this.Server = context.Server;
this.StreamGzipContents();
}
public bool IsReusable {
get {
return false;
}
}
#region private
private void StreamGzipContents() {
string cacheKey = "", cacheFile = "", content = "", enc, suffix, cachePath;
string[] plugins, languages, themes;
bool diskCache, supportsGzip, isJS, compress, core;
int i, x, expiresOffset;
GZipStream gzipStream;
Encoding encoding = Encoding.GetEncoding("windows-1252");
byte[] buff;
// Get input
plugins = GetParam("plugins", "").Split(',');
languages = GetParam("languages", "").Split(',');
themes = GetParam("themes", "").Split(',');
diskCache = true; //GetParam("diskcache", "") == "true";
isJS = GetParam("js", "") == "true";
compress = GetParam("compress", "true") == "true";
core = GetParam("core", "true") == "true";
suffix = GetParam("suffix", "min");
cachePath = Server.MapPath("."); // Cache path, this is where the .gz files will be stored
expiresOffset = 10; // Cache for 10 days in browser cache
// Custom extra javascripts to pack
string[] custom = {/*
"some custom .js file",
"some custom .js file"
*/};
// Set response headers
Response.ContentType = "text/javascript";
Response.Charset = "UTF-8";
Response.Buffer = false;
// Setup cache
Response.Cache.SetExpires(DateTime.Now.AddDays(expiresOffset));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(false);
// Vary by all parameters and some headers
Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
Response.Cache.VaryByParams["theme"] = true;
Response.Cache.VaryByParams["language"] = true;
Response.Cache.VaryByParams["plugins"] = true;
Response.Cache.VaryByParams["lang"] = true;
Response.Cache.VaryByParams["index"] = true;
// Setup cache info
if (diskCache) {
cacheKey = GetParam("plugins", "") + GetParam("languages", "") + GetParam("themes", "");
for (i = 0; i < custom.Length; i++)
cacheKey += custom[i];
cacheKey = MD5(cacheKey);
if (compress)
cacheFile = cachePath + "/tinymce.gzip-" + cacheKey + ".gz";
else
cacheFile = cachePath + "/tinymce.gzip-" + cacheKey + ".js";
}
// Check if it supports gzip
enc = Regex.Replace("" + Request.Headers["Accept-Encoding"], @"\s+", "").ToLower();
supportsGzip = enc.IndexOf("gzip") != -1 || Request.Headers["---------------"] != null;
enc = enc.IndexOf("x-gzip") != -1 ? "x-gzip" : "gzip";
// Use cached file disk cache
if (diskCache && supportsGzip && File.Exists(cacheFile)) {
Response.AppendHeader("Content-Encoding", enc);
Response.WriteFile(cacheFile);
return;
}
// Add core
if (core) {
content += GetFileContents("tinymce." + suffix + ".js");
}
// Add core languages
for (x = 0; x < languages.Length; x++)
content += GetFileContents("langs/" + languages[x] + ".js");
// Add themes
for (i = 0; i < themes.Length; i++) {
content += GetFileContents("themes/" + themes[i] + "/theme." + suffix + ".js");
for (x = 0; x < languages.Length; x++)
content += GetFileContents("themes/" + themes[i] + "/langs/" + languages[x] + ".js");
}
// Add plugins
for (i = 0; i < plugins.Length; i++) {
content += GetFileContents("plugins/" + plugins[i] + "/plugin." + suffix + ".js");
for (x = 0; x < languages.Length; x++)
content += GetFileContents("plugins/" + plugins[i] + "/langs/" + languages[x] + ".js");
}
// Add custom files
for (i = 0; i < custom.Length; i++)
content += GetFileContents(custom[i]);
// Generate GZIP'd content
if (supportsGzip) {
if (compress)
Response.AppendHeader("Content-Encoding", enc);
if (diskCache && cacheKey != "") {
// Gzip compress
if (compress) {
using (Stream fileStream = File.Create(cacheFile)) {
gzipStream = new GZipStream(fileStream, CompressionMode.Compress, true);
buff = encoding.GetBytes(content.ToCharArray());
gzipStream.Write(buff, 0, buff.Length);
gzipStream.Close();
}
} else {
using (StreamWriter sw = File.CreateText(cacheFile)) {
sw.Write(content);
}
}
// Write to stream
Response.WriteFile(cacheFile);
} else {
gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress, true);
buff = encoding.GetBytes(content.ToCharArray());
gzipStream.Write(buff, 0, buff.Length);
gzipStream.Close();
}
} else
Response.Write(content);
}
private string GetParam(string name, string def) {
string value = Request.QueryString[name] != null ? "" + Request.QueryString[name] : def;
return Regex.Replace(value, @"[^0-9a-zA-Z\-_,]+", "");
}
private string GetFileContents(string path) {
try {
string content;
path = Server.MapPath(path);
if (!File.Exists(path))
return "";
StreamReader sr = new StreamReader(path);
content = sr.ReadToEnd();
sr.Close();
return content;
} catch (Exception ex) {
// Ignore any errors
}
return "";
}
private string MD5(string str) {
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(Encoding.ASCII.GetBytes(str));
str = BitConverter.ToString(result);
return str.Replace("-", "");
}
#endregion
}
在我看来,ashx 页面正在为会话状态发送 cookie - 如果您在 asp.net 中有 cookie,它不会缓存 - 我自己也去过那里。您可以从会话中排除该处理程序,并确保它不设置任何 cookie。
我正在尝试让 TinyMCE、gzip 和缓存正常工作,但浏览器无法缓存对 gzip.ashx 处理程序的请求。
我的设置:
- TinyMCE(当前为 4.1.7)
- TinyMCE Compressor (.NET 4.0.1)
- ASP.NET WebForm 应用程序/IIS。
这是我的代码(非常标准):
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="/scripts/tinymce/tinymce.gzip.js"></script>
</head>
<body>
<script>
tinymce.init({
selector: 'textarea',
plugins: 'image link'
});
</script>
<textarea />
</body>
</html>
首次加载页面:
tinymce.gzip.js
请求 TinyMCE Compressortinymce.gzip.ashx
tinymce.gzip.ashx
压缩所有 TinyMCE javascript 文件并生成一个压缩文件 (.gz),如tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz
页面的后续加载:
tinymce.gzip.ashx
检查磁盘上是否存在 .gz 文件并将其 return 发送到浏览器
我的 tinymce.gzip.ashx(完整脚本在 post 末尾)看起来像原来的但有这个小改动,因为页面 diskcache
参数没有传入查询字符串:
..
...
themes = GetParam("themes", "").Split(',');
diskCache = true; //GetParam("diskcache", "") == "true";
isJS = GetParam("js", "") == "true";
..
无论如何,这一切工作正常,但真正的问题出现在浏览器缓存该 .gz 文件中。我永远无法得到 return 的 HTTP/1.1 304 Not Modified
响应,因此不会再次请求 .gz。所有其他文件都是 304'。
这是我尝试过的:
我已经尝试像 http://mysite/scripts/tinymce/tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz 一样直接请求 gzip,但我仍然得到
200 OK
手动设置
Response.StatusCode = 304;
只会导致响应为空,不会加载tinymce。执行
<script src="/scripts/tinymce/tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz"></script>
将 return .gz 文件但不会加载 TinyMCE
我现在已经在这上面花了五个小时 - 任何帮助都将不胜感激。
以下是 IE 11.0.9600.17498、FF 35.01 和 Fiddler 的一些屏幕截图:
完整的 tinymce.gzip.ashx 处理程序:
<%@ WebHandler Language="C#" Class="Handler" %>
/**
* tinymce.gzip.ashx
*
* Copyright, Moxiecode Systems AB
* Released under LGPL License.
*
* License: http://tinymce.moxiecode.com/license
* Contributing: http://tinymce.moxiecode.com/contributing
*
* This file compresses the TinyMCE JavaScript using GZip and
* enables the browser to do two requests instead of one for each .js file.
*
* It's a good idea to use the diskcache option since it reduces the servers workload.
*/
using System;
using System.Web;
using System.IO;
using System.IO.Compression;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
public class Handler : IHttpHandler {
private HttpResponse Response;
private HttpRequest Request;
private HttpServerUtility Server;
public void ProcessRequest(HttpContext context) {
this.Response = context.Response;
this.Request = context.Request;
this.Server = context.Server;
this.StreamGzipContents();
}
public bool IsReusable {
get {
return false;
}
}
#region private
private void StreamGzipContents() {
string cacheKey = "", cacheFile = "", content = "", enc, suffix, cachePath;
string[] plugins, languages, themes;
bool diskCache, supportsGzip, isJS, compress, core;
int i, x, expiresOffset;
GZipStream gzipStream;
Encoding encoding = Encoding.GetEncoding("windows-1252");
byte[] buff;
// Get input
plugins = GetParam("plugins", "").Split(',');
languages = GetParam("languages", "").Split(',');
themes = GetParam("themes", "").Split(',');
diskCache = true; //GetParam("diskcache", "") == "true";
isJS = GetParam("js", "") == "true";
compress = GetParam("compress", "true") == "true";
core = GetParam("core", "true") == "true";
suffix = GetParam("suffix", "min");
cachePath = Server.MapPath("."); // Cache path, this is where the .gz files will be stored
expiresOffset = 10; // Cache for 10 days in browser cache
// Custom extra javascripts to pack
string[] custom = {/*
"some custom .js file",
"some custom .js file"
*/};
// Set response headers
Response.ContentType = "text/javascript";
Response.Charset = "UTF-8";
Response.Buffer = false;
// Setup cache
Response.Cache.SetExpires(DateTime.Now.AddDays(expiresOffset));
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetValidUntilExpires(false);
// Vary by all parameters and some headers
Response.Cache.VaryByHeaders["Accept-Encoding"] = true;
Response.Cache.VaryByParams["theme"] = true;
Response.Cache.VaryByParams["language"] = true;
Response.Cache.VaryByParams["plugins"] = true;
Response.Cache.VaryByParams["lang"] = true;
Response.Cache.VaryByParams["index"] = true;
// Setup cache info
if (diskCache) {
cacheKey = GetParam("plugins", "") + GetParam("languages", "") + GetParam("themes", "");
for (i = 0; i < custom.Length; i++)
cacheKey += custom[i];
cacheKey = MD5(cacheKey);
if (compress)
cacheFile = cachePath + "/tinymce.gzip-" + cacheKey + ".gz";
else
cacheFile = cachePath + "/tinymce.gzip-" + cacheKey + ".js";
}
// Check if it supports gzip
enc = Regex.Replace("" + Request.Headers["Accept-Encoding"], @"\s+", "").ToLower();
supportsGzip = enc.IndexOf("gzip") != -1 || Request.Headers["---------------"] != null;
enc = enc.IndexOf("x-gzip") != -1 ? "x-gzip" : "gzip";
// Use cached file disk cache
if (diskCache && supportsGzip && File.Exists(cacheFile)) {
Response.AppendHeader("Content-Encoding", enc);
Response.WriteFile(cacheFile);
return;
}
// Add core
if (core) {
content += GetFileContents("tinymce." + suffix + ".js");
}
// Add core languages
for (x = 0; x < languages.Length; x++)
content += GetFileContents("langs/" + languages[x] + ".js");
// Add themes
for (i = 0; i < themes.Length; i++) {
content += GetFileContents("themes/" + themes[i] + "/theme." + suffix + ".js");
for (x = 0; x < languages.Length; x++)
content += GetFileContents("themes/" + themes[i] + "/langs/" + languages[x] + ".js");
}
// Add plugins
for (i = 0; i < plugins.Length; i++) {
content += GetFileContents("plugins/" + plugins[i] + "/plugin." + suffix + ".js");
for (x = 0; x < languages.Length; x++)
content += GetFileContents("plugins/" + plugins[i] + "/langs/" + languages[x] + ".js");
}
// Add custom files
for (i = 0; i < custom.Length; i++)
content += GetFileContents(custom[i]);
// Generate GZIP'd content
if (supportsGzip) {
if (compress)
Response.AppendHeader("Content-Encoding", enc);
if (diskCache && cacheKey != "") {
// Gzip compress
if (compress) {
using (Stream fileStream = File.Create(cacheFile)) {
gzipStream = new GZipStream(fileStream, CompressionMode.Compress, true);
buff = encoding.GetBytes(content.ToCharArray());
gzipStream.Write(buff, 0, buff.Length);
gzipStream.Close();
}
} else {
using (StreamWriter sw = File.CreateText(cacheFile)) {
sw.Write(content);
}
}
// Write to stream
Response.WriteFile(cacheFile);
} else {
gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress, true);
buff = encoding.GetBytes(content.ToCharArray());
gzipStream.Write(buff, 0, buff.Length);
gzipStream.Close();
}
} else
Response.Write(content);
}
private string GetParam(string name, string def) {
string value = Request.QueryString[name] != null ? "" + Request.QueryString[name] : def;
return Regex.Replace(value, @"[^0-9a-zA-Z\-_,]+", "");
}
private string GetFileContents(string path) {
try {
string content;
path = Server.MapPath(path);
if (!File.Exists(path))
return "";
StreamReader sr = new StreamReader(path);
content = sr.ReadToEnd();
sr.Close();
return content;
} catch (Exception ex) {
// Ignore any errors
}
return "";
}
private string MD5(string str) {
MD5 md5 = new MD5CryptoServiceProvider();
byte[] result = md5.ComputeHash(Encoding.ASCII.GetBytes(str));
str = BitConverter.ToString(result);
return str.Replace("-", "");
}
#endregion
}
在我看来,ashx 页面正在为会话状态发送 cookie - 如果您在 asp.net 中有 cookie,它不会缓存 - 我自己也去过那里。您可以从会话中排除该处理程序,并确保它不设置任何 cookie。