TinyMCE、gzip 和缓存

TinyMCE, gzip and caching

我正在尝试让 TinyMCE、gzip 和缓存正常工作,但浏览器无法缓存对 gzip.ashx 处理程序的请求。

我的设置:

这是我的代码(非常标准):

<!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>

首次加载页面:

  1. tinymce.gzip.js 请求 TinyMCE Compressor tinymce.gzip.ashx
  2. tinymce.gzip.ashx 压缩所有 TinyMCE javascript 文件并生成一个压缩文件 (.gz),如 tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz

页面的后续加载:

  1. 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'。

这是我尝试过的:

  1. 我已经尝试像 http://mysite/scripts/tinymce/tinymce.gzip-C3F36E9F5715BFD1943ECF340F1AB753.gz 一样直接请求 gzip,但我仍然得到 200 OK

  2. 手动设置Response.StatusCode = 304;只会导致响应为空,不会加载tinymce。

  3. 执行 <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。