如何为资源文件生成 ETag HTTP 标头?
7 回答
只要它在资源表示发生变化时发生变化,您如何生成它完全取决于您。
您应该尝试以另外一种方式生产它:
- 不需要您在每个条件 GET 上重新计算它,并且
- 如果资源内容未更改,则不会更改
如果您不将计算的哈希值与文件一起存储,则使用内容的哈希值可能会导致您在#1 失败。
如果您重新排列文件系统或从多个服务器提供内容,使用 inode 编号可能会导致您在 #2 失败。
一种可行的机制是使用完全依赖于内容的东西,例如 SHA-1 哈希或版本字符串,只要资源内容发生更改,就会计算和存储一次。
etag 是服务器发送给客户端的任意字符串,客户端将在下次请求文件时将其发送回服务器。
etag 应该可以基于文件在服务器上计算。有点像校验和,但您可能不想对每个发送它的文件进行校验和。
server client
<------------- request file foo
file foo etag: "xyz" -------->
<------------- request file foo
etag: "xyz" (what the server just sent)
(the etag is the same, so the server can send a 304)
我建立了一个格式为“dateamp-file size-file inode number”的字符串。因此,如果在将文件提供给客户端后在服务器上更改了文件,则如果客户端重新请求,新生成的 etag 将不匹配。
char *mketag(char *s, struct stat *sb)
{
sprintf(s, "%d-%d-%d", sb->st_mtime, sb->st_size, sb->st_ino);
return s;
}
来自http://developer.yahoo.com/performance/rules.html#etags:
默认情况下,Apache 和 IIS 都在 ETag 中嵌入数据,这大大降低了有效性测试在具有多个服务器的网站上成功的几率。
...
如果您没有利用 ETag 提供的灵活验证模型,最好完全删除 ETag。
如何在 bash 中生成默认的 apache etag
for file in *; do printf "%x-%x-%x\t$file\n" `stat -c%i $file` `stat -c%s $file` $((`stat -c%Y $file`*1000000)) ; done
即使我正在寻找与 etag 完全相同的东西(浏览器仅在服务器上发生更改时才要求文件),它也从未起作用,我最终使用了 GET 技巧(添加时间戳作为 js 文件的 get 参数)。
我一直在使用 Adler-32 作为 html 链接缩短器。我不确定这是否是一个好主意,但到目前为止,我还没有注意到任何重复。它可以用作 etag 生成器。并且它应该比尝试使用像 sha 这样的加密方案进行散列更快,但我还没有验证这一点。我使用的代码是:
shortlink = str(hex(zlib.adler32(link)+(2**32-1)/2))[2:-1]
我建议不要使用它们,而是使用最后修改的标题。
Askapache 对此有一篇有用的文章。(因为他们几乎做了所有看起来的事情!)
Mark Harrison 的代码示例类似于 Apache 2.2 中使用的代码示例。但是,当您有两台服务器具有相同的文件但文件不同时,这种算法会导致负载平衡问题inode
。这就是为什么在 Apache 2.4 中开发人员简化了 ETag 模式并删除了该inode
部分。为了使 ETag 更短,它们通常以十六进制编码:
<inttypes.h>
char *mketag(char *s, struct stat *sb)
{
sprintf(s, "\"%" PRIx64 "-%" PRIx64 "\"", sb->st_mtime, sb->st_size);
return s;
}
或者对于 Java
etag = '"' + Long.toHexString(lastModified) + '-' +
Long.toHexString(contentLength) + '"';
对于 C#
// Generate ETag from file's size and last modification time as unix timestamp in seconds from 1970
public static string MakeEtag(long lastMod, long size)
{
string etag = '"' + lastMod.ToString("x") + '-' + size.ToString("x") + '"';
return etag;
}
public static void Main(string[] args)
{
long lastMod = 1578315296;
long size = 1047;
string etag = MakeEtag(lastMod, size);
Console.WriteLine("ETag: " + etag);
//=> ETag: "5e132e20-417"
}
该函数返回与 Nginx 兼容的 ETag。查看来自不同服务器的 ETag 的比较