我需要处理一个xml文件并将其发回,将所有文件存储在内存中。我尝试将BytesIO用作类似文件的对象。 最初,我尝试了这个:
with BytesIO() as file:
data.write(file, encoding='windows-1251')
return send_file(file,attachment_filename='output.xml',as_attachment=True)
这导致了以下错误:
Traceback (most recent call last):
File "/usr/lib/python3.7/site-packages/werkzeug/wsgi.py", line 580, in __next__
data = self.file.read(self.buffer_size)
ValueError: I/O operation on closed file.
但是,当我这样做时:
with BytesIO() as file:
data.write(file, encoding='windows-1251')
file.seek(0)
return send_file(BytesIO(file.read()),attachment_filename='output.xml',as_attachment=True)
一切都很好。有人可以向我解释第一个的问题是什么以及为什么第二次尝试有效吗?
您正在使用with BytesIO()
这意味着您将对其进行处理并且 BytesIO 将保持打开状态,但您的返回在内部with
换句话说,您正在尝试发送它仍然打开,因为它的内部也with
。
在第二种情况下,您将创建 BytesIO 的另一个实例,而不使用with
这意味着它会自行关闭实例。
对不起我的英语
将文件对象视为页面和铅笔。该铅笔可以指向该页面上的任何位置。打开文件时,笔指向页面的开头(位置 0(,并且页面的内容为空(如果是打开以供输出的文件或新的BytesIO
对象(,要么包含文件的内容(如果它是打开以供输入的文件,或者是具有预加载内容的BytesIO
对象(。
当您write
文件或从文件中read
时会发生什么情况?
- 当你向文件
write
一些东西时,你只需从铅笔现在放置的位置开始,然后从那里写在页面上。假设它是一个新页面,你从左上角(位置 0(开始,然后从那里开始。当你写完后,你的铅笔停在你停止写字的地方。 - 当您从文件中
read
时,您只需从页面上读取,从铅笔现在搁置的位置开始,并通过阅读的每个字节移动铅笔来帮助自己。您可以读取到页面上的数据结束的点,此时无法进行进一步的读取(因为铅笔指向数据的末尾,并且在该点之外不存在其他数据(。
那么,在您描述的第一个场景中会发生什么?
- 你创建了一个新的
BytesIO
对象(即,你得到一个闪亮的新页面,铅笔指向开头(。 - 您将数据写入文件(即,使用铅笔将数据写入页面,完成后,铅笔指向该数据的末尾(。
- 你试图读取这些数据(不是你直接读取,而是
send_file
的实现(——但这种尝试失败了,因为铅笔在页面的末尾,没有什么可以阅读的了。
但是,在第二个方案中,您添加了一些更改:
file.seek(0)
将铅笔移动到页面的开头,因此以下read
操作将成功。- 不是
file
传给send_file
,而是传递BytesIO(file.read())
。首先,您使用file.read()
读取文件 - 由于上面的file.seek(0)
,该文件成功。接下来,获取刚刚读取的数据并将其传递给BytesIO
构造函数 - 因此它会生成一个新页面,该内容预加载在页面上,铅笔仍指向其开头(因此read
ing将成功(。
实际上,在上述 2 个更改中,只需要第一个 - 第二个更改是多余的,实际上会降低性能,因为数据被读取和写入两次。正确的方法是:
with BytesIO() as file:
data.write(file, encoding='windows-1251')
file.seek(0)
return send_file(file, attachment_filename='output.xml', as_attachment=True)