Node.js中的etag包知识学习之express的配置etag

 原文地址 :阅读原文

  API为:etag(entity, [options])

   这个方法为给定的实体产生一个strong etag。这个方法必须获取这个实体的完整内容。可以是Buffer,可以是fs.Stats。默认情况下,这个strong etag的产生是不需要fs.Stats的,因为这个fs.Stats会产生weak Etag而不是strong etag,不过这种默认行为可以通过options.weak来覆盖!
   options:

    weak:指定产生的Etag是否会包含弱验证符号(即开头的W/),如果是true那么会包含字符串"W/"。默认是false,除非我们传入的实体是fs.Stats,这时候weak就是true了表示有开头的弱验证符号,而且是通过文件的内容来产生的etag值!

问题1:etag的API中第一个参数是不是必须的?

//必须含有参数
  if (entity == null) {
    throw new TypeError('argument entity is required')
  }
问题2:第一个参数必须是Buffer,fs.Stats或者String?

 // support fs.Stats object
  var isStats = isstats(entity)
  //判断是否是文件描述符
  var weak = options && typeof options.weak === 'boolean'
    ? options.weak
    : isStats
  //如果用户传入了weak,那么用用户的weak为主,否则weak保存的就是是否是文件描述符
  // validate argument
  if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) {
    throw new TypeError('argument entity must be string, Buffer, or fs.Stats')
  }

很显然,上面的问题的答案yes。那么他是如何判断一个对象是否为文件描述符呢?

function isstats(obj) {
  // genuine fs.Stats
  //判断是否是原生的文件描述符,通过instanceof判断
  if (typeof Stats === 'function' && obj instanceof Stats) {
    return true
  }
  // quack quack
  //n. 庸医;鸭叫声 adj. 骗人的;冒牌医生 vi. (鸭子)嘎嘎叫;吹嘘;大声闲聊
  //如果不是原生的文件描述符,那么判断这个对象是否有ctime,同时ctime(change time)是Date对象,是否有mtime,同时mtime(modified time)是Date
  //是否有ino,而且是number,是否有size同时是number!
  return obj && typeof obj === 'object'
    && 'ctime' in obj && toString.call(obj.ctime) === '[object Date]'
    && 'mtime' in obj && toString.call(obj.mtime) === '[object Date]'
    && 'ino' in obj && typeof obj.ino === 'number'
    && 'size' in obj && typeof obj.size === 'number'
}
很显然是通过instanceof或者判断一个对象的ctime,mtime,ino和size属性来完成的!
问题3:如果是文件描述符和不是文件描述符有什么区别?

var tag = isStats
    ? stattag(entity)
    : entitytag(entity)
  //如果是文件描述符那么用stattag来产生tag,否则用entitytag
  return weak
    ? 'W/' + tag
    : tag
如果是文件描述符那么是通过stattag来完成,否则用entitytag来完成的
 //如果传入了文件描述符那么获取他的mtime(modified time)和size来产生etag,很显然这时候的etag产生不需要文件的内容
 //而只是用文件描述符的mtime和size!
function stattag(stat) {
  var mtime = stat.mtime.getTime().toString(16)
  var size = stat.size.toString(16)
  return '"' + size + '-' + mtime + '"'
}
也就是说如果是文件描述符那么 只是用的mtime,size来产生etag
/**
 * Generate an entity tag.
 * @param {Buffer|string} entity
 * @return {string}
 * @private
 */
function entitytag(entity) {
  if (entity.length === 0) {
    // fast-path empty
    //如果传入的内容为空那么返回的etag为固定值
    return '"0-1B2M2Y8AsgTpgAmY7PhCfg"'
  }
  // compute hash of entity
  var hash = crypto
    .createHash('md5')
    .update(entity, 'utf8')
    .digest('base64')
    .replace(base64PadCharRegExp, '')
   //用文件的内容来产生etag,同时取消其中连续的=号!
  // compute length of entity
  var len = typeof entity === 'string'
    ? Buffer.byteLength(entity, 'utf8')
    : entity.length
    //如果传入的参数是string那么获取这个string的字节长度,如果不是string那么直接获取他的length属性
  return '"' + len.toString(16) + '-' + hash + '"'
  //所以返回的值为其长度的16进制编码,同时加上内容的hash值!
}
如果不是文件描述符那么这时候是通过" 内容的长度的十六进制+文件内容的md5的16进制来完成的"!
问题4:那么是不是如果是文件描述符就返回mtime+size来返回的etag,否则就返回内容长度的十六进制+文件内容的md5的16进制?

<pre name="code" class="javascript">  var weak = options && typeof options.weak === 'boolean'
    ? options.weak
    : isStats
  return weak
    ? 'W/' + tag
    : tag

 

很显然会有如下的结论:

              

问题5:为什么要看这部分的原码,为什么要仔细学习etag部分?

这是因为express中的setting中有一个选项就是etag,这个etag的产生就是通过这个包来完成的,我们看看里面他关于etag的配置有那些?


我们看到其中etag可以为布尔值表示是否开启etag,而strong表示开启strong Etag,反之表示weak,当然也可以指定一个函数,这个函数让自己控制对etag的生成!仔细阅读etag

这部分的原码如下:

'use strict'

/**
 * Module exports.
 * @public
 */

module.exports = etag
var crypto = require('crypto')
var Stats = require('fs').Stats
//引入node.js的crypto模块和fs模块
var base64PadCharRegExp = /=+$/
var toString = Object.prototype.toString
/**
 * Generate an entity tag.
 * @param {Buffer|string} entity
 * @return {string}
 * @private
 */
function entitytag(entity) {
  if (entity.length === 0) {
    // fast-path empty
    //如果传入的内容为空那么返回的etag为固定值
    return '"0-1B2M2Y8AsgTpgAmY7PhCfg"'
  }
  // compute hash of entity
  var hash = crypto
    .createHash('md5')
    .update(entity, 'utf8')
    .digest('base64')
    .replace(base64PadCharRegExp, '')
   //用文件的内容来产生etag,同时取消其中连续的=号!
  // compute length of entity
  var len = typeof entity === 'string'
    ? Buffer.byteLength(entity, 'utf8')
    : entity.length
    //如果传入的参数是string那么获取这个string的字节长度,如果不是string那么直接获取他的length属性
  return '"' + len.toString(16) + '-' + hash + '"'
  //所以返回的值为其长度的16进制编码,同时加上内容的hash值!
}

/**
 * Create a simple ETag.
 *
 * @param {string|Buffer|Stats} entity
   //第一个参数可以是String,Buffer,Stats
 * @param {object} [options]
 //第二个参数是一个对象,其中的weak属性是boolean
 * @param {boolean} [options.weak]
 //函数返回值为String
 * @return {String}
 * @public
 */
function etag(entity, options) {
  //必须含有参数
  if (entity == null) {
    throw new TypeError('argument entity is required')
  }
  // support fs.Stats object
  var isStats = isstats(entity)
  //判断是否是文件描述符
  var weak = options && typeof options.weak === 'boolean'
    ? options.weak
    : isStats
  //如果用户传入了weak,那么用用户的weak为主,否则weak保存的就是是否是文件描述符
  // validate argument
  if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) {
    throw new TypeError('argument entity must be string, Buffer, or fs.Stats')
  }
  // generate entity tag
  var tag = isStats
    ? stattag(entity)
    : entitytag(entity)
  //如果是文件描述符那么用stattag来产生tag,否则用entitytag
  return weak
    ? 'W/' + tag
    : tag
}

/**
 * Determine if object is a Stats object.
 *
 * @param {object} obj
 * @return {boolean}
 * @api private
 */

function isstats(obj) {
  // genuine fs.Stats
  //判断是否是原生的文件描述符,通过instanceof判断
  if (typeof Stats === 'function' && obj instanceof Stats) {
    return true
  }
  // quack quack
  //n. 庸医;鸭叫声 adj. 骗人的;冒牌医生 vi. (鸭子)嘎嘎叫;吹嘘;大声闲聊
  //如果不是原生的文件描述符,那么判断这个对象是否有ctime,同时ctime(change time)是Date对象,是否有mtime,同时mtime(modified time)是Date
  //是否有ino,而且是number,是否有size同时是number!
  return obj && typeof obj === 'object'
    && 'ctime' in obj && toString.call(obj.ctime) === '[object Date]'
    && 'mtime' in obj && toString.call(obj.mtime) === '[object Date]'
    && 'ino' in obj && typeof obj.ino === 'number'
    && 'size' in obj && typeof obj.size === 'number'
}

/**
 * Generate a tag for a stat.
 *
 * @param {object} stat
 * @return {string}
 * @private
 */
 //如果传入了文件描述符那么获取他的mtime(modified time)和size来产生etag,很显然这时候的etag产生不需要文件的内容
 //而只是用文件描述符的mtime和size!
function stattag(stat) {
  var mtime = stat.mtime.getTime().toString(16)
  var size = stat.size.toString(16)
  return '"' + size + '-' + mtime + '"'
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值