【问题标题】:How to generate short unique names for uploaded files in nodejs如何在nodejs中为上传的文件生成简短的唯一名称
【发布时间】:2015-06-18 18:48:55
【问题描述】:

我需要用简短的唯一标识符命名上传的文件,例如 nYrnfYEv a4vhAoFG hwX6aOr7。如何确保文件的唯一性?

【问题讨论】:

  • 来自这篇文章的第一条评论:Warning: None of the answers have a true-random result! They are only pseudo-random. When using random strings for protection or security, don't use any of them!!! Try one of these api's我需要真正独特的名字
  • @Erik 你现在可以查看我的答案
  • 检查shortid。听起来和您要找的完全一样。
  • 是否生成唯一不重复的ID?

标签: node.js file-upload uniqueidentifier


【解决方案1】:

更新: shortid 已弃用。请改用Nano ID。下面的答案也适用于 Nano ID。


(发布我的 cmets 作为答案,并回复您的疑虑)

您可能想查看shortid NPM 模块,它生成类似于您发布的示例的短 id(令人震惊,我知道 :))。结果是可配置的,但默认情况下它是一个 7 到 14 个字符之间的字符串(长度也是随机的),所有 URL 友好(正则表达式中的A-Za-z0-9\_\-)。

回答您(和其他发帖人)的疑虑:

  • 除非您的服务器具有真正的随机数生成器(极不可能),否则每个解决方案都将使用 PRNG(伪随机数生成器)。 shortid 使用 Node.js 加密模块来生成 PRNG 数字,然而,它比 Math.random() 更好的生成器
  • shortid 不是连续的,这使得猜测它们更加困难
  • 虽然不保证shortid 是唯一的,但发生冲突的可能性非常小。除非您每年生成数十亿条条目,否则您可以放心地假设永远不会发生冲突。
  • 对于大多数情况,依靠概率相信不会发生冲突就足够了。如果您的数据太重要以至于不能冒那么一点点的风险,您可以通过在其前面加上时间戳来使 shortid 基本上 100% 唯一。作为一个额外的好处,文件名也将更难猜测。 (注意:我写了“基本上 100% 唯一”,因为理论上,如果在同一时间戳(即同一秒)中生成两个项目,您仍然可能会发生冲突。但是,我永远不会担心这一点。要有一个真正 100% 确定您唯一的选择是检查数据库或文件系统,但这需要更多资源。)
  • shortid 本身不这样做的原因是,对于大多数应用程序来说,发生冲突的可能性太小而不必担心,而拥有尽可能短的 id 更为重要。

【讨论】:

  • @RichardPoole 这正是我建议添加时间戳作为选项的原因(绝对没有必要)。在不同的秒数内,理论上可以再次生成相同的 ID。这当然取决于您生成了多少个 ID,并且我在上面提出的所有观点都是有效的
  • 我一直在使用的一种方法,无论使用哪种生成器(例如,它可能是 shortid 或 xxhash),每次上传文件时都会递增一个整数计数器,它基本上会跟踪数字自服务器运行以来上传的文件的数量,并将此计数器插入文件名的种子中。使用这种方法与插入 unix 时间戳相结合,我看不到任何可能的冲突,除非您在没有重新启动服务器的情况下上传 2^53 个文件。
  • 事实上,如果使用这种方法发生碰撞,一秒钟内必须上传 2^53 个文件,我很确定这是不可能的。
  • @Gaboik1问题在于您需要一种存储唯一索引的方法 - 例如数据库。每次 ID 生成都需要两次 DB 调用:一次读取索引,另一次更新索引。使用 shortId (et similia) 加上时间戳,碰撞的机会已经非常小,即使是大规模的。它也是无状态的,因此适合与分布式系统一起使用,没有集中的数据库/存储库,这将是一个瓶颈。
【解决方案2】:
function uniqueFileName( filePath, stub)
{
    let id = 0;
    let test = path.join(filePath, stub + id++);
    while (fs.existsSync(test))
    {
        test = path.join(filePath, stub + id++);
    }
    return test;
}

【讨论】:

    【解决方案3】:

    非常简单的代码。产生一个几乎唯一的文件名 或者如果这还不够,请检查文件是否存在

    function getRandomFileName() {
    var timestamp = new Date().toISOString().replace(/[-:.]/g,"");  
    var random = ("" + Math.random()).substring(2, 8); 
    var random_number = timestamp+random;  
    return random_number;
    }
    

    【讨论】:

      【解决方案4】:

      一种选择是生成唯一标识符 (UUID) 并相应地重命名文件。

      看看kelektiv/node-uuid npm 模块。


      示例:

      $ npm install uuid
      

      ...然后在您的 JavaScript 文件中:

      const uuidv4 = require('uuid/v4'); // I chose v4 ‒ you can select others
      var filename = uuidv4(); // '110ec58a-a0f2-4ac4-8393-c866d813b8d1'
      

      任何时候你执行uuidv4(),你都会得到一个very-fresh-new-one

      注意:还有其他选择/类型的 UUID。阅读模块的文档以熟悉这些内容。

      【讨论】:

      • 我需要为上传的文件命名。
      • @Erik:这就是原因。你从某个地方收到一些流,当你保存它时,你命名它。即便如此,你也可以重命名它。
      • @Machina 是的,但我仍然需要生成唯一名称
      • 为什么是 sudo?恕我直言,npm install --save node-uuid
      • 缩短 UUID 是个坏主意,因为它不能保证缩短 UUID 时的唯一性。
      【解决方案5】:

      尝试以下 sn-p:-

          function getRandomSalt() {
          var milliseconds = new Date().getTime();
          var timestamp = (milliseconds.toString()).substring(9, 13)
          var random = ("" + Math.random()).substring(2, 8);
          var random_number = timestamp+random;  // string will be unique because timestamp never repeat itself
          var random_string = base64_encode(random_number).substring(2, 8); // you can set size here of return string
          var return_string = '';
          var Exp = /((^[0-9]+[a-z]+)|(^[a-z]+[0-9]+))+[0-9a-z]+$/i;
          if (random_string.match(Exp)) {                 //check here whether string is alphanumeric or not
              return_string = random_string;
          } else {
              return getRandomSalt();  // call recursivley again
          }
          return return_string;
      }
      

      根据您的要求,文件名可能具有唯一性的字母数字名称。基于当前时间时间戳概念的唯一名称,因为当前时间在未来永远不会重复,为了使其更强大,我应用了base64encode,它将其转换为字母数字。

         var file = req.files.profile_image;
         var tmp_path = file.path;
         var fileName = file.name;
         var file_ext = fileName.substr((Math.max(0, fileName.lastIndexOf(".")) || Infinity) + 1);
         var newFileName = getRandomSalt() + '.' + file_ext;
      

      谢谢

      【讨论】:

      • getRandomSalt 总是生成相同的字符串
      • 感谢您的代码。我们得到多少重复的 id?
      • 从不。它是时间戳和随机数的组合。在完成base64编码之后。你可以检查数十亿的数据,然后验证我的答案。
      • 如果你选择大字符串作为getRandomSalt函数的返回字符串就好了
      • 关于时间戳从不重复的评论可能具有误导性。当我输入for(i=0; i < 60; ++i) console.log(new Date().getTime()); 时,我可以看到时间戳重复了几十次。如果您在保证其余代码消耗超过一毫秒的机器上运行,我想评论是正确的。无论哪种方式,大多数时间戳字符串都不会改变,因此对唯一性的贡献很小。我在节点上运行它并在前 60 次迭代中发生了冲突。运行 600 次,堆栈溢出(尝试证明递归停止!)。
      【解决方案6】:

      我认为您可能对真随机和伪随机感到困惑。

      伪随机字符串“通常表现出统计随机性,同时由完全确定性的随机过程生成”。这意味着,如果您在加密应用程序中使用这些随机值作为熵,您就不想使用伪随机生成器。

      不过,对于您的使用,我相信它会很好 - 只需检查潜在的(极不可能的)冲突。

      您想要做的只是创建一个随机字符串 - 不确保它是 100% 安全且完全随机的。

      【讨论】:

      • 除非你有一个 TRNG 硬件模块,这是极不可能的,否则每个软件解决方案都使用一个 PRNG。然而,有些“更随机”:例如,使用 Node.js crypto 模块是比 Math.random() 更好的 PRNG。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多