纯前端文件名生成算法(七牛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
效果
点击查看在线效果
相关文章
|
2月前
|
搜索推荐 前端开发 数据可视化
【优秀python web毕设案例】基于协同过滤算法的酒店推荐系统,django框架+bootstrap前端+echarts可视化,有后台有爬虫
本文介绍了一个基于Django框架、协同过滤算法、ECharts数据可视化以及Bootstrap前端技术的酒店推荐系统,该系统通过用户行为分析和推荐算法优化,提供个性化的酒店推荐和直观的数据展示,以提升用户体验。
105 1
|
13天前
|
算法 前端开发 机器人
一文了解分而治之和动态规则算法在前端中的应用
该文章详细介绍了分而治之策略和动态规划算法在前端开发中的应用,并通过具体的例子和LeetCode题目解析来说明这两种算法的特点及使用场景。
一文了解分而治之和动态规则算法在前端中的应用
|
28天前
|
前端开发 JavaScript 开发者
Express.js与前端框架的集成:React、Vue和Angular的示例与技巧
本文介绍了如何将简洁灵活的Node.js后端框架Express.js与三大流行前端框架——React、Vue及Angular进行集成,以提升开发效率与代码可维护性。文中提供了详细的示例代码和实用技巧,展示了如何利用Express.js处理路由和静态文件服务,同时在React、Vue和Angular中构建用户界面,帮助开发者快速掌握前后端分离的开发方法,实现高效、灵活的Web应用构建。
38 3
|
13天前
|
算法 前端开发
一文了解贪心算法和回溯算法在前端中的应用
该文章深入讲解了贪心算法与回溯算法的原理及其在前端开发中的具体应用,并通过分析LeetCode题目来展示这两种算法的解题思路与实现方法。
|
2月前
|
搜索推荐 前端开发 算法
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
本文介绍了一个基于用户画像和协同过滤算法的音乐推荐系统,使用Django框架、Bootstrap前端和MySQL数据库构建,旨在为用户提供个性化的音乐推荐服务,提高推荐准确性和用户满意度。
117 7
基于用户画像及协同过滤算法的音乐推荐系统,采用Django框架、bootstrap前端,MySQL数据库
|
2月前
|
前端开发 JavaScript 开发者
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
本文提供了一个前端JS按钮点击事件、弹出式窗口和遮罩层的实战示例,包括HTML、CSS和JavaScript的具体实现代码,以及功能解析,演示了如何实现按钮点击后触发弹窗显示和遮罩层,并在2秒后自动关闭或点击遮罩层关闭弹窗的效果。
前端JS按钮点击事件、跳出弹窗、遮罩的实战示例
|
2月前
|
JavaScript 算法 前端开发
"揭秘Vue.js的高效渲染秘诀:深度解析Diff算法如何让前端开发快人一步"
【8月更文挑战第20天】Vue.js是一款备受欢迎的前端框架,以其声明式的响应式数据绑定和组件化开发著称。在Vue中,Diff算法是核心之一,它高效计算虚拟DOM更新时所需的最小实际DOM变更,确保界面快速准确更新。算法通过比较新旧虚拟DOM树的同层级节点,递归检查子节点,并利用`key`属性优化列表更新。虽然存在局限性,如难以处理跨层级节点移动,但Diff算法仍是Vue高效更新机制的关键,帮助开发者构建高性能Web应用。
54 1
|
2月前
|
机器学习/深度学习 人工智能 算法
【人工智能】传统语音识别算法概述,应用场景,项目实践及案例分析,附带代码示例
传统语音识别算法是将语音信号转化为文本形式的技术,它主要基于模式识别理论和数学统计学方法。以下是传统语音识别算法的基本概述
61 2
|
2月前
|
数据采集 前端开发 算法
基于朴素贝叶斯算法的新闻类型预测,django框架开发,前端bootstrap,有爬虫有数据库
本文介绍了一个基于Django框架和朴素贝叶斯算法开发的新闻类型预测系统,该系统具备用户登录注册、后台管理、数据展示、新闻分类分布分析、新闻数量排名和新闻标题预测等功能,旨在提高新闻处理效率和个性化推荐服务。
|
2月前
|
前端开发 开发者 Apache
揭秘Apache Wicket项目结构:如何打造Web应用的钢铁长城,告别混乱代码!
【8月更文挑战第31天】Apache Wicket凭借其组件化设计深受Java Web开发者青睐。本文详细解析了Wicket项目结构,帮助你构建可维护的大型Web应用。通过示例展示了如何使用Maven管理依赖,并组织页面、组件及业务逻辑,确保代码清晰易懂。Wicket提供的页面继承、组件重用等功能进一步增强了项目的可维护性和扩展性。掌握这些技巧,能够显著提升开发效率,构建更稳定的Web应用。
81 0