HTTP:生成ETag头

31

如何为资源文件生成ETag HTTP标头?

7个回答

19
只要每当资源表述发生变化时就随之更新,如何生成它完全由您决定。
您应该尝试以一种方式生成它,使其满足以下两个条件:
1.不需要在每次有条件的 GET 请求中重新计算它;
2.如果资源内容没有更改,它不会发生变化。
使用内容哈希可能会导致 #1 失败,如果您没有将计算出的哈希值与文件一起存储。
使用 inode 号可能会导致 #2 失败,如果您重新排列文件系统或从多个服务器提供内容。
一种可行的机制是使用完全基于内容的东西,例如 SHA-1 哈希或版本字符串,在每次资源内容更改时计算并存储一次。

17

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)

我建立了一个字符串,格式为“日期时间戳-文件大小-文件inode号”。因此,如果服务器上的文件在向客户端提供服务后发生更改,则新生成的etag在客户端重新请求时将无法匹配。

char *mketag(char *s, struct stat *sb)
{
    sprintf(s, "%d-%d-%d", sb->st_mtime, sb->st_size, sb->st_ino);
    return s;
}

2
如果mtime是文件上次更改的时间,那么size和inode有什么作用? - Steve
在我的情况下,这是因为它是从CGI程序计算出来的路径。你说得对,在直接路径的情况下,mtime可能已经足够了。由于成本主要在于stat(),因此包括inode和size不会产生额外费用,这可能会防止(当然很少见)一个流氓管理员更新文件并将其触摸回原始的mtime。 - Mark Harrison
@MarkHarrison,为什么你需要在 etag 周围加上双引号?这是语法规定的一部分吗? - Pacerier

7

来自http://developer.yahoo.com/performance/rules.html#etags

默认情况下,Apache和IIS都会嵌入数据在ETag中,这会大大降低在具有多个服务器的网站上进行有效性测试成功的几率。

...

如果您没有利用ETag提供的灵活验证模型,最好完全删除ETag。


2
如何在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技巧(将时间戳作为GET参数添加到JS文件中)。


1
我一直在使用Adler-32作为HTML链接缩短工具。我不确定这是否是一个好主意,但到目前为止,我还没有注意到任何重复的情况。它可以作为ETag生成器。并且它应该比尝试使用像SHA这样的加密方案进行哈希更快,但我还没有验证过。我使用的代码是:
 shortlink = str(hex(zlib.adler32(link)+(2**32-1)/2))[2:-1]

0

嗯,真是遗憾,希望他们能尽快恢复,因为该网站是一个宝藏般的建议来源! - Rich Bradshaw
@RichBradshaw,只是一个问题,我们如何知道资源的最后修改时间戳?这完全是在查看数据库更改历史记录后手动操作吗?还是有自动化的方法? - Supun Wijerathne

0

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比较


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接