返回

后会无期之 jshttp 解析(二):ETag 生成原理

前端

我们昨天解读了 fresh 这个库,了解了服务器是如何对比文件是否更新了,其中用到了 ETag,那么今天我们就趁热打铁,了解下 ETag 是怎么生成的,同样是来自 jshttp 的 ETag 库。也就是说,资源发生改变的时候,ETag 的值一定发生了改变,那么 ETag 又是如何生成的呢?

内容变化,当然离不开算 Hash 值,但是 ETag 生成并非直接对内容计算 Hash 值,而是对内容的 Buffer 对象计算 Hash 值。对此我们先来了解下 Buffer 对象。

Buffer 对象

Buffer 对象是 Node.js 中处理二进制数据的对象。它是一个类似于数组的对象,可以通过索引来访问二进制数据,但与数组不同,Buffer 对象中的元素都是无符号 8 位整数。也就是说,Buffer 对象可以被看作一个字节数组,其中每个元素的值在 0 到 255 之间。

创建 Buffer 对象有以下几种方式:

  • 通过 Buffer.from(data) 创建:data 可以是字符串、ArrayBuffer、Array、Buffer 等。
  • 通过 Buffer.alloc(size) 创建:创建一个指定大小的 Buffer 对象,填充为 0。
  • 通过 Buffer.allocUnsafe(size) 创建:创建一个指定大小的 Buffer 对象,不进行初始化。

ETag 生成

ETag 的生成就是对 Buffer 对象计算 Hash 值,jshttp 库使用的是 `crypto` 模块中的 `createHash()` 函数。`createHash()` 函数接受一个算法参数,指定要使用的哈希算法,默认值为 "sha1",即 SHA-1 算法。

ETag 生成的代码如下:

export function generate(buf: Buffer, weak?: boolean): string {
  // 弱 ETag,一般用于代理服务器
  if (weak) {
    return `W/"${createHash(buf).toString("base64")}"`;
  }
  // 强 ETag
  return `"${createHash(buf).toString("base64")}"`;
}

function createHash(buf: Buffer) {
  return crypto.createHash("sha1").update(buf).digest();
}

以上代码中,`createHash()` 函数创建了一个 SHA-1 哈希对象,然后调用 `update()` 函数更新哈希对象,并将 Buffer 对象作为参数传递。最后,调用 `digest()` 函数获取哈希值,并使用 Base64 编码转换成字符串。

生成的 ETag 是一个用双引号包裹的 Base64 编码字符串,如果指定了 `weak` 参数,则会在 ETag 前面添加一个 "W/" 前缀,表示这是一个弱 ETag。

至此,我们就了解了 ETag 是如何生成的,在下一篇文章中,我们将继续探讨 ETag 的使用场景和相关应用。