当文件名由 ASP.NET Web API 控制器确定时如何使用 IIS 压缩和缓存文件

How to compress and cache files with IIS when filename is determined by ASP.NET Web API controller

我有一个 ASP.NET Web API,它具有以下控制器操作,用于提供可能以 JSON 格式存储在磁盘某处的大型 (<1-200MB) 3D 模型(在网络根之外):

public HttpResponseMessage GetModel(Guid accessToken, Guid modelId)
{
    Guid projectId = MyBackend.Validate(accessToken);
    string fileName = MyPaths.GetModelFile(projectId, modelId);

    HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
    response.Content = new StringContent(File.ReadAllText(fileName),
                                         Encoding.UTF8, "application/json");
    return response;
}

在我的 Global.asax 中有这一行:

GlobalConfiguration.Configuration.MessageHandlers.Insert(
        0, new ServerCompressionHandler(new GZipCompressor(), new DeflateCompressor()));

当我调用 API 端点时,内容按预期进行了 gzip 压缩,但 IIS (8.5) 不缓存压缩结果,因此它必须为每个请求重新压缩相关文件。我希望它没有被缓存,因为控制器将 JSON 读入内存并将其用作 StringContent.

我对 IIS 几乎一无所知,对 ASP.NET Web API 知之甚少。我怎样才能最好地提供 JSON 文件以便压缩它们并确保 IIS 缓存压缩结果,这样它们就不必每次都重新压缩? (如果出于某种原因这听起来不是个好主意,我应该怎么做?)

我建议创建一个您可以完全控制它的缓存,而不是将压缩委托给 IIS,或者在本例中,Web API 消息处理程序。这是优化 CPU 使用牺牲磁盘 space 的最佳方式。

在您的服务器上创建一个目录,并在其中保存包含压缩内容的临时文件。然后,在您的 API 请求中,检查您的 3D 模型的压缩文件是否已经创建。如果没有,请即时创建它,并将其存储在磁盘上。下次请求模型时,结果已经计算出来,您只需要 return 压缩文件内容。

// Calculate a path for your cache file.
var compressedCacheFilename = GetCachePath(filename);
Stream compressedStream;
if (File.Exists(compressedCacheFilename)) {
    compressedStream = new FileStream(compressedCacheFilename, FileMode.Open, FileAccess.Read);
}
else {
    /* Use the best compression algorithm for your data
       and store it to the corresponding path. */
    compressedStream = compressor.CompressTo(
        new FileStream(filename, FileMode.Open, FileAccess.Read),
        compressedCacheFilename
    );
}

HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
response.Content = new StreamContent(compressedStream);
response.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
response.Content.Headers.ContentEncoding.Clear();
response.Content.Headers.ContentEncoding.Add("gzip"); // your compression algorithm

请注意,在代码示例中,我使用 StreamContent 而不是 StringContent,因为第一个使用的内存较少。同样重要的是要知道这是假设模型不会改变。为此,您也可能需要磁盘的最大容量。