无权写入我的临时文件

61

我试图在Windows操作系统上使用Python创建和写入临时文件。我使用了Python模块tempfile来创建一个临时文件。

但是当我尝试写入这个临时文件时,出现了一个Permission Denied错误。我不能写入临时文件吗?! 我做错了什么吗? 如果我想要在Python中创建和写入临时文件,应该怎么做呢?出于安全考虑,我想在临时目录中创建一个临时文件,而不是在本地(在.exe执行的目录中)创建。

IOError: [Errno 13] Permission denied: 'c:\\users\\blah~1\\appdata\\local\\temp\\tmpiwz8qw'

temp = tempfile.NamedTemporaryFile().name
f = open(temp, 'w') # error occurs on this line
9个回答

82

NamedTemporaryFile实际上为您创建并打开文件,因此您无需再次打开它进行写操作。

事实上,Python文档中指出:

在命名临时文件仍处于打开状态时,第二次使用名称打开文件的可行性因平台而异(在Unix上可以这样做;在Windows NT或更高版本上则不行)。

这就是你遇到权限错误的原因。您可能想要的是类似于以下内容:

f = tempfile.NamedTemporaryFile(mode='w') # open file
temp = f.name                             # get name (if needed)

1
这篇文章 https://dev59.com/12855IYBdhLWcg3wiUz1 中有一个有用的澄清和进一步信息。 - Iain Hunter
在这种情况下,你需要使用delete=False - Reinderien
1
@Reinderien,如果你希望文件在关闭后仍然存在,那么你只需要使用delete=False。但是这样的话,它就不再是一个临时文件了,对吗?我猜你的评论更适合那些建议先关闭再尝试重新打开的答案,而不是针对这个答案。 - undefined
@Reinderien,只有在你想在关闭后保留文件才需要delete=False。但这样就不是真正的临时文件了,对吧?我猜你的评论更适合那些建议关闭后再尝试重新打开的答案,而不是针对这个答案。 - paxdiablo

43

请按以下方式使用delete参数:

tmpf = NamedTemporaryFile(delete=False)

但是,完成后您需要手动删除临时文件。

tmpf.close()
os.unlink(tmpf.name)

缺陷参考:https://github.com/bravoserver/bravo/issues/111

致意, Vidyesh


3
即使使用with语句,我是否仍需要手动删除? - Nuclear03020704
@Nuclear03020704 是的,即使使用“with”语句,文件也不会被删除。 - Savostyanov Konstantin

11
考虑使用os.path.join(tempfile.gettempdir(), os.urandom(24).hex())替代。这是一个可靠的、跨平台的方案,唯一需要注意的是它不适用于FAT分区。
NamedTemporaryFile存在许多问题,其中最小的问题之一是可能由于权限错误无法创建文件,然后无法检测到权限错误,接着会循环数百万次,从而挂起你的程序和文件系统。

1
非常感谢。我使用pysmb从文件共享服务器加载文件,并且当使用tempfile.NamedTemporaryFile()时,某些文件只得到了空内容。通过切换到with open(filename, 'wb') as fp,问题得到了解决。 - Harry Duong

5
以下是对命名临时文件的自定义实现,基于Erik Aronesty原始回答进行了扩展:
import os
import tempfile


class CustomNamedTemporaryFile:
    """
    This custom implementation is needed because of the following limitation of tempfile.NamedTemporaryFile:

    > Whether the name can be used to open the file a second time, while the named temporary file is still open,
    > varies across platforms (it can be so used on Unix; it cannot on Windows NT or later).
    """
    def __init__(self, mode='wb', delete=True):
        self._mode = mode
        self._delete = delete

    def __enter__(self):
        # Generate a random temporary file name
        file_name = os.path.join(tempfile.gettempdir(), os.urandom(24).hex())
        # Ensure the file is created
        open(file_name, "x").close()
        # Open the file in the given mode
        self._tempFile = open(file_name, self._mode)
        return self._tempFile

    def __exit__(self, exc_type, exc_val, exc_tb):
        self._tempFile.close()
        if self._delete:
            os.remove(self._tempFile.name)

1
这里有一个使用垃圾回收器的代码,不确定是否是个好主意,但似乎“更接近”现有的ntf:https://gist.github.com/earonesty/a052ce176e99d5a659472d0dab6ea361 - Erik Aronesty

1

tempfile.NamedTemporaryFile(): 它为您创建并打开一个临时文件。

f = open(temp,'w'): 您将再次打开已经打开的文件,这就是为什么会出现权限被拒绝错误的原因。

如果您真的想要重新打开该文件,则首先需要关闭它,方法如下 -

temp= tempfile.NamedTemporaryFile()
temp.close()
f = open(temp.name, 'w')

0

这个问题可能比许多人想象中的更加复杂。不管怎样,以下是我的解决方案:

  1. 利用atexit模块
def delete_files(files):
    for file in files:
        file.close()
        os.unlink(file.name)

2. 让 NamedTemporaryFile 的参数 delete=False
temp_files = []
 
result_file = NamedTemporaryFile(dir=tmp_path(), suffix=".xlsx", delete=False)
self.temp_files.append(result_file)
  1. delete_files注册为清理函数
atexit.register(delete_files, temp_files)

0

另一种选择是在tempfile.TemporaryDirectory中创建一个文件。

这种方法可能看起来比使用tempfile.TemporaryFile(实际上在Windows上是一个NamedTemporaryFile)要繁琐一些,但它不需要delete=False,因此目录和文件将会被自动清理。

以原始问题为例:

with tempfile.TemporaryDirectory() as temp_dir:
    with open(f'{temp_dir}/temp.file', 'w') as f:
        f.write('success')

这里有一个使用`pathlib`的例子,为了方便起见。
with tempfile.TemporaryDirectory() as temp_dir:
    temp_file_path = pathlib.Path(temp_dir, 'temp.file')
    temp_file_path.write_text('success')

# just to show that everything is cleaned up automatically
assert not temp_file_path.exists()

0
这是有效的。
T = tempfile.NamedTemporaryFile(delete=False)
z1 = open(T.name)
z2 = open(T.name)
z2 = open(T.name, mode='w')
...

我检查了,你可以使用所有的读描述符进行阅读,并使用所有的写描述符进行写入

在Windows 10、10.0.19045以及Python 3.11上测试通过

附注:当文件不再需要时,请不要忘记删除它:

os.unlink(T.name)

记住,所有文件描述符必须关闭


-4

权限被拒绝,因为文件在您代码的第2行处打开。

首先使用f.close()关闭它,然后您可以开始在临时文件上写入内容。


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