如何使用Python将临时文件持久化到磁盘?

30

我尝试使用'tempfile'模块来操作和创建文本文件。一旦文件准备好,我想将其保存到磁盘上。我以为这只需要使用'shutil.copy'就可以了。但是,我遇到了一个'permission denied' IOError:

>>> import tempfile, shutil
>>> f = tempfile.TemporaryFile(mode ='w+t')
>>> f.write('foo')
>>> shutil.copy(f.name, 'bar.txt')

Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    shutil.copy(f.name, 'bar.txt')
  File "C:\Python25\lib\shutil.py", line 80, in copy
    copyfile(src, dst)
  File "C:\Python25\lib\shutil.py", line 46, in copyfile
    fsrc = open(src, 'rb')
IOError: [Errno 13] Permission denied: 'c:\\docume~1\\me\\locals~1\\temp\\tmpvqq3go'
>>> 

使用'tempfile'库时是否不打算这样做?有更好的方法吗? (也许我忽略了一些非常微不足道的东西)

4个回答

45

hop 是正确的,而 dF. 对错误发生的原因不正确。

由于你还没有调用 f.close(),所以文件尚未被删除。

NamedTemporaryFile文档说:

在命名的临时文件仍然打开的情况下,能否使用其名称再次打开文件取决于平台(在 Unix 上可以这样做,在 Windows NT 或更高版本上则不行)。

对于 TemporaryFile

在 Unix 下,文件的目录条目在创建文件后立即被删除。其他平台不支持此功能;您的代码不应该依赖使用此函数创建的临时文件是否在文件系统中具有可见名称。

因此,要使临时文件持久化(在 Windows 上),您可以执行以下操作:

import tempfile, shutil
f = tempfile.NamedTemporaryFile(mode='w+t', delete=False)
f.write('foo')
file_name = f.name
f.close()
shutil.copy(file_name, 'bar.txt')
os.remove(file_name)
Hans Sjunnesson提供的解决方案也不正确,因为copyfileobj仅将文件类对象复制到另一个文件类对象,而非文件名:

shutil.copyfileobj(fsrc, fdst[, length])

将文件类对象fsrc的内容复制到文件类对象fdst中。如果给出整数长度,则是缓冲区大小。特别地,负长度值表示在不分块地循环源数据的情况下复制数据;默认情况下,数据会以块的形式读取,以避免无法控制的内存消耗。请注意,如果fsrc对象的当前文件位置不为0,则仅从当前文件位置到文件结尾的内容将被复制。


1
@jedierikb os.remove 可以工作 - 或者你可以在 "all done" 后直接关闭 NamedTemporaryFile 而不使用 delete=False - K Z
3
如果没有使用delete=Falseshutil.copy函数就不能正常工作,会导致原来的IOError错误。因此,在这种情况下,我同意@jedierikb的观点,你需要使用os.remove函数。 - cod3monk3y
另外,从NamedTemporaryFile返回的对象是类似文件的对象,因此它可以shutil.copyfileobj一起使用。 - cod3monk3y

23

使用TemporaryFileNamedTemporaryFile创建的文件在关闭时会自动删除,这就是为什么你会收到一个错误。如果你不想这样,可以使用mkstemp替代(请参阅tempfile文档)。

>>> import tempfile, shutil, os
>>> fd, path = tempfile.mkstemp()
>>> os.write(fd, 'foo')
>>> os.close(fd)
>>> shutil.copy(path, 'bar.txt')
>>> os.remove(path)

3
我认为错误源于在文件仍处于打开状态时无法第二次访问它(根据文档,这只在 Windows 操作系统上是如此)。 - user3850
6
Kay Z在下面给出了正确的答案。此外,他/她解释得更好。 - Dave Abrahams
谢谢。这个答案很有见地,因为它让我知道如何创建一个临时目录,而不会在 with/as 语句结束时被删除,就像 mkstemp 一样,mkdtemp 的工作方式也很相似。 - Brōtsyorfuzthrāx
1
TemporaryFile 打开文件句柄,而 shutil 尝试第二次打开该文件进行写入,但在 Windows 上无法正常工作,正如 @hop 指出的那样。 - cod3monk3y
这不是原因,你能否编辑你的答案使其正确? - smci

14

从Python 2.6开始,您还可以使用NamedTemporaryFile并将delete=选项设置为False。这样,即使在关闭文件后,临时文件也仍然可访问。

请注意,在Windows(NT及更高版本)上,您无法在文件仍然打开时第二次访问该文件。您必须在复制之前关闭它。这在Unix系统上不成立。


如果设置delete=False,Shutil可以正常工作。然而,os.replace或os.rename仍然无法正常工作。 - Sid133

6
您可以始终使用shutil.copyfileobj,在您的示例中:
new_file = open('bar.txt', 'rw')
shutil.copyfileobj(f, new_file)

我认为这是正确的答案,但有一点需要注意:你可能需要先添加 'f.seek(0)'。 - squanto773
你能否详细说明一下'rw'模式与'r+'和'w+'有何不同?实际上我无法使用它,我得到一个ValueError错误消息,提示它必须恰好有一个创建/读取/写入/追加模式... - Jyrkka
Jyrrka:哇,这是一个来自过去的冲击!如果你查看open()的文档:https://docs.python.org/3/library/functions.html#open 那么你会发现模式的不同在于它们是否截断文件。 - Hans Sjunnesson
是的,在Python 3中,rw似乎是一种无效的模式,你使用rw的意图是什么?(使用w不就足够了吗?) - jave.web
@HansSjunnesson 是的,在Python 3中,rw似乎是一种无效的模式,你使用rw的意图是什么?难道不只用w版本就足够了吗?**wb**对我来说可行 :) - jave.web

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接