【问题标题】:Generate temporary file names without creating actual file in Python生成临时文件名而不在 Python 中创建实际文件
【发布时间】:2014-12-19 21:47:09
【问题描述】:

Best way to generate random file names in Python 的答案展示了如何在 Python 中创建临时文件。

在我的情况下,我只需要一个临时文件名。
在实际文件创建后调用tempfile.NamedTemporaryFile() 返回一个文件句柄。

有没有办法只获取文件名?我试过这个:

# Trying to get temp file path
tf = tempfile.NamedTemporaryFile()
temp_file_name = tf.name
tf.close()
# Here is my real purpose to get the temp_file_name
f = gzip.open(temp_file_name ,'wb')
...

【问题讨论】:

  • NamedTemporaryFile 保证一个唯一的名称,(可能)通过尝试它并在它存在时重试。仅获得一个名称并不能保证您以后可以实际创建文件,您将面临其他使用相同名称的人的竞争条件。
  • @Joachim 没错,这里存在竞争条件,最好避免这种情况。但是,有时您必须将临时文件名传递给函数(文件打开发生在内部。)拥有一个很好的随机名称可以使竞争条件成为非问题的可能性更大。我认为有必要提供一个好的临时文件名,以尽量减少竞争条件失败的机会。当然,根据正在运行的进程和正在执行的任务添加一个好的前缀和后缀将提供更少的冲突机会。
  • @PolyMesh 您可以通过创建一个临时目录然后在其中使用固定名称文件来避免竞争条件。所以你的函数接受一个目录,而不是一个文件,并且总是创建同一个文件。
  • 使用 tarfile 并将其传递给 fileobj
  • @DylanYoung:虽然很聪明,但将工作隔离到一个临时目录仍然会将您的代码暴露给竞争。即使您明确更改该目录的权限以排除竞争用户,您也可以永远排除超级用户或同一用户的竞争进程 - 当然,您只会引入更微妙的竞赛(例如,如果竞争进程创建了相同的临时目录并在您有机会之前更改了该目录的权限)。混淆临时文件名并紧紧抓住你的 GNU 手册可能更明智。

标签: python temporary-files


【解决方案1】:

如果你只想要一个临时文件名,你可以调用内部临时文件函数_get_candidate_names():

import tempfile

temp_name = next(tempfile._get_candidate_names())
% e.g. px9cp65s

再次调用next,将返回另一个名称等。这不会为您提供临时文件夹的路径。要获取默认的 'tmp' 目录,请使用:

defult_tmp_dir = tempfile._get_default_tempdir()
% results in: /tmp 

【讨论】:

  • 创建临时目录的更好方法是temp_dir = tempfile.mkdtemp(prefix='some-prefix_'),它将安全地创建一个临时目录并返回一个带有绝对路径的字符串。
  • 需要指出的是next(tempfile._get_candidate_names())不一定返回不存在的路径,这就是为什么用户级临时文件接口可以try several names until an unused one is found:
  • 可以使用公共tempfile.gettempdir() 而不是私人tempfile._get_default_tempdir()
  • @EmanuelEy 重要的是要记住,当使用tempfile.mkdtemp 时,用户负责删除临时目录及其内容。
【解决方案2】:

我认为最简单、最安全的方法是:

path = os.path.join(tempfile.mkdtemp(), 'something')

创建了一个只有您可以访问的临时目录,因此应该没有安全问题,但不会在其中创建任何文件,因此您可以选择要在该目录中创建的任何文件名。请记住,您仍然必须删除该文件夹。

编辑:在 Python 3 中,您现在可以使用 tempfile.TemporaryDirectory() 作为上下文管理器来为您处理删除:

with tempfile.TemporaryDirectory() as tmp:
  path = os.path.join(tmp, 'something')
  # use path

【讨论】:

  • 正如 Daniel Braun 上面提到的:重要的是要记住,在使用 tempfile.mkdtemp 时,用户负责删除临时目录及其内容。
  • 如果您使用tempfile.TemporaryDirectory() 作为上下文管理器,它将为您删除。
  • 我真的很喜欢这个,因为它会自动处理删除。就我而言,为 shutil.make_archive 函数创建一个临时文件名很有用,因为它需要存档的基本名称作为输入,而不是类似文件的对象。
【解决方案3】:

可能有点晚了,但这有什么问题吗?

import tempfile
with tempfile.NamedTemporaryFile(dir='/tmp', delete=False) as tmpfile:
    temp_file_name = tmpfile.name
f = gzip.open(temp_file_name ,'wb')

【讨论】:

  • 这段代码实际上会创建临时文件以获得它的名字,而在问题中它说without creating actual file in Python
  • 这不能回答问题
  • 完美地为我工作。谢谢!
【解决方案4】:

tempfile.mktemp() 这样做。

但请注意,它已被弃用。但是,与使用 _get_candidate_names() 相比,它不会创建文件,并且它是 tempfile 中的公共函数。

不推荐使用它的原因是由于调用它和实际尝试创建文件之间的时间间隔。但是在我的情况下,这种可能性很小,即使失败也是可以接受的。但您可以自行评估您的用例。

【讨论】:

  • “即使失败也是可以接受的”;竞争条件不仅是失败的风险,还是安全风险(请参阅tempfile.mktemp 文档)。所以这不应该被认为是可以接受的。
  • @bignose 这是一个潜在的安全问题。这取决于你想做什么,你所处的执行环境等。也就是说:做类似os.path.join(tempfile.mkdtemp(), 'something') 这样的事情可能更安全,至少创建了目录(并且归你所有,我想) .
【解决方案5】:

结合前面的答案,我的解决方案是:

def get_tempfile_name(some_id):
    return os.path.join(tempfile.gettempdir(), next(tempfile._get_candidate_names()) + "_" + some_id)

如果您不需要,请将 some_id 设为可选。

【讨论】:

  • 同样,候选名称可能实际上并不可用。这是正确答案:stackoverflow.com/a/45803022/6387880
  • 但是,可能需要创建随机名称。尽管如此,可以肯定的是,如果_get_candidate_names() 不存在,可以默认使用一些半随机字符串生成器。例如一些 uuid。
【解决方案6】:

正如 Joachim Isaksson 在 cmets 中所说,如果您只是获得一个名称,如果其他程序碰巧在您的程序使用该名称之前使用该名称,您可能会遇到问题。机会渺茫,但并非不可能。

所以在这种情况下安全的做法是使用完整的 GzipFile() 构造函数,它具有签名

GzipFile( [filename[, mode[, compresslevel[, fileobj]]]])

因此,您可以将打开的文件obj 和文件名传递给它,如果您愿意的话。详情请见gzip docs

【讨论】:

    【解决方案7】:

    我会这样做:

    import tempfile
    import os.path
    import random
    import string
    
    def generate_temp_filename() -> str:
        random_string = ''.join(random.choices(string.ascii_uppercase + string.digits, k=10))
        return os.path.join(tempfile.gettempdir(), random_string)
    

    优于其他答案:

    • 不依赖私有函数(以_开头的函数)
    • 不创建文件或目录
    • 不使用已弃用的函数

    【讨论】:

    • ...不错,年轻的学徒。您仍在与竞争进程竞争,但至少您不再使用已弃用和/或私有的代码库破坏自己的代码库stdlib 实用程序函数。如果目录创建不是一个令人信服的问题,我会给the tempfile.TemporaryDirectory() approach 稍微推一下这个问题。在这一点上,这是一个选择你的毒药的问题,真的。
    • 值得更多点赞。这就是我的目标。
    【解决方案8】:
    from random import sample
    from string import digits, ascii_letters
    from tempfile import gettempdir
    from pathlib import Path
    
    filename = Path(gettempdir()) / ''.join(sample(ascii_letters + digits, 10))
    ## PosixPath('/tmp/fHyMSeVsY8') or
    ## 
    filename = Path(gettempdir()).joinpath(''.join(sample(ascii_letters + digits, 10))).as_posix()
    ## '/tmp/fHyMSeVsY8'
    
    f = gzip.open(filename ,'wb')
    

    【讨论】:

      猜你喜欢
      • 2016-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-05
      • 2021-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多