【问题标题】:nodejs - Temporary file namenodejs - 临时文件名
【发布时间】:2011-08-14 04:46:30
【问题描述】:

在 node.js 中,如何生成唯一的临时文件名,例如 mkstemp(3)?我想用fs.rename原子地写一个文件。

【问题讨论】:

  • 这不是工具推荐。这是一个API是否存在的问题。我的意思是什么鬼,90% 的 SO 问题都是关于是否存在一些 API 来做某事。全部关闭或不关闭。

标签: node.js


【解决方案1】:

另一个流行的包是tmp

【讨论】:

【解决方案2】:

也许您在此期间已经找到node-temp

【讨论】:

  • @k0pernikus 你可以在图书馆网站上找到最新的例子
【解决方案3】:

不使用任何额外的插件:

var crypto = require('crypto');
var fs = require('fs'); 

var filename = 'foo'+crypto.randomBytes(4).readUInt32LE(0)+'bar';
fs.writeFileSync(filename, 'baz');

编辑:读取 cmets。

【讨论】:

  • 小心,这是一个非常危险的经典反模式!多年来,这种代码导致了许多漏洞,而这正是创建 mkstemp 来替换 mktemp 的确切原因。为安全起见,应使用 O_CREAT 和 O_EXCL 标志打开文件,以防止其他用户在您写入之前预测文件名并在那里创建符号链接。我想如果您绝对确定其他用户无法在一百万年内预测文件名,那没有必要,但为什么要碰巧呢? en.wikipedia.org/wiki/Symlink_race
  • 我认为该文件名中没有足够的熵,它输出 foo1492796329bar 带有随机盐的 SHA1 会更好
  • Josue,我同意 32 位的熵太少了。不过,对于短期文件来说,64 位应该足够了。如果我们已经有了随机值,为什么还要建议使用哈希?
  • 鉴于其他 2 个答案建议安装在安装时运行任意代码的任意代码,随时间任意更改,依赖于其他几个包并且过度设计,无论如何这种方法更好。
  • @Geoff 加密安全随机字节无法预测。唯一的问题是用户应该这样做:crypto.randomBytes(16).toString('base64').replace(/\//,'_')。根据我的经验,mktemp 存在严重的跨平台问题以及可能的挂起/中断问题。
【解决方案4】:

试试这个功能,安全无漏洞。节点 8.x LTS

function tempFile (name = 'temp_file', data = '', encoding = 'utf8') {
    const fs = require('fs');
    const os = require('os');
    const path = require('path');

    return new Promise((resolve, reject) => {
        const tempPath = path.join(os.tmpdir(), 'foobar-');
        fs.mkdtemp(tempPath, (err, folder) => {
            if (err) 
                return reject(err)

            const file_name = path.join(folder, name);

            fs.writeFile(file_name, data, encoding, error_file => {
                if (error_file) 
                    return reject(error_file);

                resolve(file_name)
            })
        })
    })
}

解析临时文件的路径,拒绝 mkdtemp 或 writeFile 错误

// C:\Users\MYPC\AppData\Local\Temp\foobar-3HmKod\temp_file
// /temp/Temp/foobar-3HmKod/temp_file
tempFile().then(path => console.log(path)).catch(e => console.log("error", e)) //or

// C:\Users\MYPC\AppData\Local\Temp\foobar-9KHuxg\hola.txt
// /temp/Temp/foobar-9KHuxg/hola.txt
tempFile('hola.txt', 'hello there').then(path => console.log(path)).catch(e => console.log("e", e))

【讨论】:

  • 声称这段代码是“安全且没有漏洞的”是没有道理的。
【解决方案5】:

在 node 和 python 中创建临时文件会受到涉及权限更改和跨平台问题的竞争条件的影响,尤其是在测试 ACL 具有挑战性的 Windows 上。

结果是它可以直接挂起您的机器,以许多现代语言请求临时文件。 (如果节点无权访问临时目录,取决于 ACL,它可以获得 EEXIST,即使文件不存在 - 结果可能是无限循环。

最简单的解决方案是使用足够的熵以使冲突的可能性可以忽略不计(在密码学意义上)。

这还具有使临时文件创建在所有平台上的安全性的副作用,而无需大量审查源代码。只要您的随机数是安全的,就无法预测将用于篡夺权限或访问的文件。

它有助于需要传递文件名而不是文件句柄的程序。

const crypto = require('crypto');
const os = require('os'); 
const path = require('path'); 

function tmpFile(prefix, suffix, tmpdir) {
    prefix = (typeof prefix !== 'undefined') ? prefix : 'tmp.';
    suffix = (typeof suffix !== 'undefined') ? suffix : '';
    tmpdir = tmpdir ? tmpdir : os.tmpdir();
    return path.join(tmpdir, prefix + crypto.randomBytes(16).toString('hex') + suffix);
}

唯一的缺点是这在 FAT 格式的分区上不起作用,这在 USB 记忆棒上仍然很常见。

【讨论】:

    【解决方案6】:

    类似于kinematic's answer,但有 2 个字节的额外熵和字母而不是数字:

    import Crypto from 'crypto';
    import {tmpdir} from 'os';
    import Path from 'path';
    
    function tmpFile(ext) {
        return Path.join(tmpdir(),`archive.${Crypto.randomBytes(6).readUIntLE(0,6).toString(36)}.${ext}`);
    }
    

    用法:

    const file = tmpFile('tar.gz'); // "/tmp/archive.1scpz5ew5d.tar.gz"
    

    我正在创建档案,所以我选择“档案”作为基本名称,但您可以根据需要更改它。

    【讨论】:

    • 16 字节是一个更安全的数字 - 即使用例极端也不会发生冲突
    • @ErikAronesty 当然,但在撰写本文时节点不支持大于 6 字节的整数。您可以将 16 字节与...结合使用,实际上我们的上限仍为 64 位 (readBigInt64LE)。看起来他们还没有添加任意长度的 bigint 方法,但是如果你想要 128 位,你可以分 2 块来做。
    • 我将 Crypto.randomBytes(16) 转换为十六进制,然后收工。安全、快速且不成问题。
    • 哦,是的,您可以直接从缓冲区转换为十六进制或base64,效果很好。 crypto.randomBytes(16).toString('hex')
    • Path.join(tmpdir(),`${prefix}${Crypto.randomBytes(16).toString('hex')}${suffix}`);}
    【解决方案7】:

    使用 npm 包tempfile

    import tempfile from 'tempfile';
    

    或者

    let tempfile = "";
    import('tempfile').then(a => {
        tempfile = a.default;
    });
    

    然后:

    let filename = tempfile() //e,g: /tmp/5fc8a50f-e9cd-4420-b62a-e365b3ef2c2a
    let imagefilename = tempfile(".jpeg") // e,g: /tmp/5a6dea08-a704-4fa8-9d0a-8ffa5e134543.jpeg
    

    【讨论】:

      猜你喜欢
      • 2023-03-31
      • 1970-01-01
      • 2016-06-13
      • 2012-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多