sal*_*ius 5 python io file shutil fastapi
我需要保存上传到我的服务器的文件(最大文件大小为 10MB)并找到了这个答案,它工作得很好。但是,我想知道使用该模块的意义是什么shutil,这之间有什么区别:
file_location = f"files/{uploaded_file.filename}"
with open(file_location, "wb+") as file_object:
file_object.write(uploaded_file.file.read())
Run Code Online (Sandbox Code Playgroud)
和这个:
import shutil
file_location = f"files/{uploaded_file.filename}"
with open(file_location, "wb+") as file_object:
shutil.copyfileobj(uploaded_file.file, file_object)
Run Code Online (Sandbox Code Playgroud)
在我的编程经历中,我多次遇到shutil模块,但仍然无法弄清楚它的好处read()和write()方法。
我想强调关于OP的问题和@Tim Roberts的(当前接受的)答案的几点:
\n“shutil 分块复制,以便您可以复制大于内存的文件”。您还可以使用 \xe2\x80\x94 分块复制文件请read()查看下面的简短示例,以及此和此答案以了解更多\n详细信息\xe2\x80\x94 就像您可以将整个文件加载到内存\n使用shutil.copyfileobj(),通过给出负值length。
with open(uploaded_file.filename, \'wb\') as f:\n while contents := uploaded_file.file.read(1024 * 1024): # adjust the chunk size as desired\n f.write(contents)\nRun Code Online (Sandbox Code Playgroud)\n在底层,copyfileob()使用与上述非常相似的方法,即文件对象的read()利用和方法;因此,如果您使用其中一种而不是另一种,那几乎没有什么区别。其源代码 如下所示。如果在 Wnidows 上运行,则默认缓冲区大小(即下面)设置为(字节),或者在其他平台上设置为(字节)(请参阅此处write() copyfileob()COPY_BUFSIZE1MB1024 *102464KB64 * 1024)。
def copyfileobj(fsrc, fdst, length=0):\n """copy data from file-like object fsrc to file-like object fdst"""\n if not length:\n length = COPY_BUFSIZE\n # Localize variable access to minimize overhead.\n fsrc_read = fsrc.read\n fdst_write = fdst.write\n while True:\n buf = fsrc_read(length)\n if not buf:\n break\n fdst_write(buf)\nRun Code Online (Sandbox Code Playgroud)\n“shutil有按名称复制文件的例程,因此您根本不必打开它们...”由于 OP 似乎使用FastAPI\nframework(实际上是\n下面的Starlette),UploadFile因此公开了一个实际的 Python\n SpooledTemporaryFile(一个类似文件的对象),您可以使用 \nattribute 获取该对象(源代码可以在此处.file找到)。当 FastAPI/Starlette 创建 的新实例时,它已经在幕后创建了该实例,该实例保持打开状态。因此,由于您正在处理一个在文件系统 \xe2\x80\x94 中没有可见名称的临时文件,否则该文件将允许您在不使用已打开的 \xe2\ x80\x94 打开文件的情况下复制内容,使用或 都没有区别。UploadFileSpooledTemporaryFileshutilread()copyfileobj()
“它可以保留权限、所有权和创建/修改/访问时间戳。” 尽管这是关于保存通过 Web 框架上传的文件\xe2\x80\x94,因此,大多数元数据不会根据文档与文件\xe2\x80\x94 一起传输,但上述声明不是完全正确:
\n\n\n警告:即使是更高级别的文件复制功能(
\nshutil.copy()、shutil.copy2())也无法复制所有文件\n元数据。在 POSIX 平台上,这意味着文件所有者和组以及 ACL 都会丢失。在 Mac OS 上,不使用资源分支和其他元数据。这意味着资源将丢失,文件类型和创建者代码将不正确。在 Windows 上,不会复制文件\n所有者、\nACL 和备用数据流。
\n
话虽这么说,使用 并没有什么问题copyfileobj()。相反,如果您正在处理大文件,并且您希望避免将整个文件加载到内存中\xe2\x80\x94,因为您可能没有足够的 RAM 来容纳所有数据\xe2\x80\x94,并且您宁愿使用copyfileobj()而不是使用类似的解决方案read()(如上面第 1 点所述),使用shutil.copyfileobj(fsrc, fdst). 此外,copyfileobj()已提供(自 Python 3.8 起)作为替代的依赖于平台的高效复制操作。length您可以通过调整中的参数来更改默认缓冲区大小copyfileobj()。
如果copyfileobj()在 FastAPI(同步)端点内使用def,则完全没问题,因为defFastAPI 中的普通端点在等待的外部线程池中运行,而不是直接调用(因为它会阻塞服务器)。另一方面,async def端点在主(单)线程上运行,因此,调用这样的方法,即copyfileobj()执行阻塞 I/O 操作(如源代码所示)将导致阻塞整个服务器(对于有关defvs的更多信息async def,请查看此答案)。copyfileobj()因此,如果您要从端点内部调用async def,则应确保在单独的线程中运行此操作\xe2\x80\x94 以及所有其他文件操作,例如open()和close()\xe2\x80\x94,以确保主线程(运行协程的地方)不会被阻塞。您可以使用 Starlette 来完成此操作,当您调用对象的方法时, run_in_threadpool()FastAPI 也会在内部使用 Starlette ,如此处所示。例如:asyncUploadFile
await run_in_threadpool(shutil.copyfileobj, fsrc, fdst)\nRun Code Online (Sandbox Code Playgroud)\n有关更多详细信息和代码示例,请查看此答案。
\n您的方法要求整个文件都在内存中。 shutil分块复制,以便您可以复制大于内存的文件。此外,shutil还具有按名称复制文件的例程,因此您根本不必打开它们,并且它可以保留权限、所有权和创建/修改/访问时间戳。
| 归档时间: |
|
| 查看次数: |
3162 次 |
| 最近记录: |