纯前端文件名生成算法(七牛ETag算法)示例

简介: 这几天在研究唯一文件名生成与文件特征验证解决方案,之前都是使用MD5算法,但是除了MD5外还有没有其他办法呢?后来无意看到了七牛云的ETag就稍微研究了下。

这几天在研究唯一文件名生成与文件特征验证解决方案,之前都是使用MD5算法,但是除了MD5外还有没有其他办法呢?后来无意看到了七牛云的ETag就稍微研究了下。
七牛ETag算法说明:https://developer.qiniu.com/kodo/manual/1231/appendix#qiniu-etag
github:https://github.com/qiniu/qetag

1.算法原理

七牛的 hash/etag 算法是公开的。算法大体如下:

  • 小于或等于4M的文件
1. 对文件内容做sha1计算;
  +---------------+
  |     <=4MB     |
  +---------------+
   \      |      /
    \   sha1()  /
     \    |    /
      \   V   /
    +--+-----+
    |1B| 20B |              2. 在sha1值(20字节)前拼上单个字节,值为0x16;
    +--+-----+
     |  |
     |  \--- 文件内容的sha1值 
     |
     \------ 固定为0x16
3. 对拼接好的21字节的二进制数据做url_safe_base64计算,所得结果即为ETag值。
  • 大于4M的文件
1. 对文件内容按4M大小切块;
2. 对每个块做sha1计算;
         +----------+----------+-------
         |    4MB   |   4MB    | ...
         +----------+----------+-------
          \    |    |   |     /
           \ sha1() | sha1() /
            \  |    |   |   /
             \ V    |   V  /
              +-----+-----+-------
              | 20B | 20B | ...
              +-----+-----+-------
               \      |      /
                \   sha1()  /
                 \    |    /
                  \   V   /
                +--+-----+
                |1B| 20B |      3. 对所有的 sha1 值拼接后做二次 sha1,
                +--+-----+         然后在二次 sha1 值前拼上单个字节,值为0x96;
                 |  |
                 |  \---- 二次sha1的值
                 \------- 固定为0x96
4. 对拼接好的21字节的二进制数据做url_safe_base64计算,所得结果即为ETag值。
  • 为何需要公开 hash/etag 算法?
    这个和 “消重” 问题有关,详细见:如何避免用户上传相同的文件
  • 为何在 sha1 值前面加一个字节的标记位0x160x96
    0x16 = 22,而 2^22 = 4M。所以前面的 0x16 其实是文件按 4M 分块的意思。
    0x96 = 0x80 | 0x16。其中 0x80 表示这个文件是大文件(有多个分块),hash 值也经过了2重的 sha1 计算

2.qetag.js使用教程(NodeJS)

前往(https://github.com/qiniu/qetag)下载源码

img_18d387fd54daf14b906ec2973fb90820.png

使用示例:

//demo.js
var getEtag = require("./qetag");
var fs = require("fs")

fs.readFile("文件路径", function (err, buf) { 
    getEtag(buf, function (v) {
        console.log(v);
    })
});
/////////或
getEtag("文件路径", function (v) {
     console.log(v);
})
img_af2d3da69f59fb27c46778d937053730.png

3.纯前端JS实现qETag

其实我们已经知道原理和NodeJS的源文件了我们只要稍微改一下就可以了
完整源码在:https://gitee.com/baojuhua/lutils/blob/master/others/qetag.js

1.sha1算法

2.Uint8Array与原生Array来替代Buffer操作

  • 我们观察源码中的Buffer操作
    img_79740a6095d0cca7f6635c92586263ff.png
  • 我们稍微改一下,用Uint8Array原生Array替代Buffer
    img_2f2d502a0ddc587158b59fa1fbdeb8d1.png

3.完整源码

  • qetag.js
function getEtag(buffer, callback) {
    // sha1算法
    var shA1 =  sha1.digest;

    // 以4M为单位分割
    var blockSize = 4 * 1024 * 1024;
    var sha1String = [];
    var prefix = 0x16;
    var blockCount = 0;

    var bufferSize = buffer.size || buffer.length || buffer.byteLength;
    blockCount = Math.ceil(bufferSize / blockSize);

    for (var i = 0; i < blockCount; i++) {
        sha1String.push(shA1(buffer.slice(i * blockSize, (i + 1) * blockSize)));
    }
    function concatArr2Uint8(s) {//Array 2 Uint8Array
        var tmp = [];
        for (var i of s) tmp = tmp.concat(i);
        return new Uint8Array(tmp);
    }
    function Uint8ToBase64(u8Arr, urisafe) {//Uint8Array 2 Base64
        var CHUNK_SIZE = 0x8000; //arbitrary number
        var index = 0;
        var length = u8Arr.length;
        var result = '';
        var slice;
        while (index < length) {
            slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length));
            result += String.fromCharCode.apply(null, slice);
            index += CHUNK_SIZE;
        }
        return urisafe ? btoa(result).replace(/\//g, '_').replace(/\+/g, '-') : btoa(result);
    }
    function calcEtag() {
        if (!sha1String.length) return 'Fto5o-5ea0sNMlW_75VgGJCv2AcJ';
        var sha1Buffer = concatArr2Uint8(sha1String);
        // 如果大于4M,则对各个块的sha1结果再次sha1
        if (blockCount > 1) {
            prefix = 0x96;
            sha1Buffer = shA1(sha1Buffer.buffer);
        } else {
            sha1Buffer = Array.apply([], sha1Buffer);
        }
        sha1Buffer = concatArr2Uint8([[prefix], sha1Buffer]);
        return Uint8ToBase64(sha1Buffer, true);
    }
    return (calcEtag());
}

4.使用示例

  • demo.html
<!-- https://github.com/emn178/js-sha1 -->
<script src="./sha1.js"></script> 
<script src="./qetag.js"></script>
<script>
    function fload(input) {
        var fs = input.files;
        if (fs.length) {
            var f = fs[0];
            var reader = new FileReader(); 
            reader.onload = function () { 
                document.getElementById("etagDemo").innerHTML = getEtag(this.result);
            }
            reader.readAsArrayBuffer(f);
        }
    }
</script>
<input id="f" type="file" onchange="fload(this)" />
<div id="etagDemo"></div>
img_8cbd310c46806d1629d8423aa4d33233.gif
效果
点击查看在线效果
相关文章
|
8天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
37 3
|
1月前
|
算法
分享一些提高二叉树遍历算法效率的代码示例
这只是简单的示例代码,实际应用中可能还需要根据具体需求进行更多的优化和处理。你可以根据自己的需求对代码进行修改和扩展。
|
2月前
|
NoSQL 前端开发 MongoDB
前端的全栈之路Meteor篇(三):运行在浏览器端的NoSQL数据库副本-MiniMongo介绍及其前后端数据实时同步示例
MiniMongo 是 Meteor 框架中的客户端数据库组件,模拟了 MongoDB 的核心功能,允许前端开发者使用类似 MongoDB 的 API 进行数据操作。通过 Meteor 的数据同步机制,MiniMongo 与服务器端的 MongoDB 实现实时数据同步,确保数据一致性,支持发布/订阅模型和响应式数据源,适用于实时聊天、项目管理和协作工具等应用场景。
|
2月前
|
移动开发 算法 前端开发
前端常用算法全解:特征梳理、复杂度比较、分类解读与示例展示
前端常用算法全解:特征梳理、复杂度比较、分类解读与示例展示
33 0
|
3月前
|
算法 前端开发 机器人
一文了解分而治之和动态规则算法在前端中的应用
该文章详细介绍了分而治之策略和动态规划算法在前端开发中的应用,并通过具体的例子和LeetCode题目解析来说明这两种算法的特点及使用场景。
一文了解分而治之和动态规则算法在前端中的应用
|
2月前
|
前端开发
前端常用方法防抖(debounce)和节流(throttle)的示例演示及应用场景说明
前端常用方法防抖(debounce)和节流(throttle)的示例演示及应用场景说明
38 0
|
3月前
|
前端开发 JavaScript 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
71 3
|
3月前
|
算法 前端开发
一文了解贪心算法和回溯算法在前端中的应用
该文章深入讲解了贪心算法与回溯算法的原理及其在前端开发中的具体应用,并通过分析LeetCode题目来展示这两种算法的解题思路与实现方法。
|
4月前
|
前端开发 JavaScript 开发者
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
本文提供了一个前端JS按钮点击事件、弹出式窗口和遮罩层的实战示例,包括HTML、CSS和JavaScript的具体实现代码,以及功能解析,演示了如何实现按钮点击后触发弹窗显示和遮罩层,并在2秒后自动关闭或点击遮罩层关闭弹窗的效果。
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
|
4月前
|
JavaScript 算法 前端开发
"揭秘Vue.js的高效渲染秘诀:深度解析Diff算法如何让前端开发快人一步"
【8月更文挑战第20天】Vue.js是一款备受欢迎的前端框架,以其声明式的响应式数据绑定和组件化开发著称。在Vue中,Diff算法是核心之一,它高效计算虚拟DOM更新时所需的最小实际DOM变更,确保界面快速准确更新。算法通过比较新旧虚拟DOM树的同层级节点,递归检查子节点,并利用`key`属性优化列表更新。虽然存在局限性,如难以处理跨层级节点移动,但Diff算法仍是Vue高效更新机制的关键,帮助开发者构建高性能Web应用。
82 1