在有人说这是重复之前,我认为这不是因为我看过类似的问题并且他们没有帮助我!
我在python中创建一个Flask服务器,我需要能够有一个显示pdf的URL。
我尝试使用以下代码:
@app.route('/pdf')
def pdfStuff():
with open('pdffile.pdf', 'rb') as static_file:
return send_file(static_file, attachment_filename='pdffile.pdf')
当我转到/pdf
时,它会显示pdf文件pdffile.pdf
。
但是,这不起作用,因为当我运行代码时出现此错误:
ValueError: I/O operation on closed file
情况怎么样?我的return语句在with语句中,因此不应该打开文件吗?
我尝试使用普通static_file = open(...)
并使用try
和finally
语句,如下所示:
static_file = open('pdffile.pdf','rb')
try:
return send_file(static_file, attachment_filename='pdffile.pdf')
finally:
static_file.close()
上面的代码发生了同样的错误,我不明白为什么。有谁知道我可能做错了什么?
很抱歉,如果我是愚蠢的,并且有一些简单的事情我犯了错误!
非常感谢你!!
答案 0 :(得分:3)
将send_file
与文件名一起使用,按照预期的方式打开,投放和关闭。
@app.route('/pdf')
def pdfStuff():
return send_file('pdffile.pdf')
答案 1 :(得分:-1)
尽管@iurisilvio的回答解决了这个特定的问题,但在任何其他情况下都不是一个有用的答案。我自己也在为此而苦苦挣扎。
以下所有示例都抛出ValueError: I/O operation on closed file.
,为什么?
@app.route('/pdf')
def pdfStuff():
with open('pdffile.pdf', 'rb') as static_file:
return send_file(static_file, attachment_filename='pdffile.pdf')
@app.route('/pdf')
def pdfStuff():
static_file = open('pdffile.pdf','rb')
try:
return send_file(static_file, attachment_filename='pdffile.pdf')
finally:
static_file.close()
我正在做些不同的事情。像这样:
@page.route('/file', methods=['GET'])
def build_csv():
# ... some query ...
ENCODING = 'utf-8'
bi = io.BytesIO()
tw = io.TextIOWrapper(bi, encoding=ENCODING)
c = csv.writer(tw)
c.writerow(['col_1', 'col_2'])
c.writerow(['1', '2'])
bi.seek(0)
return send_file(bi,
as_attachment=True,
attachment_filename='file.csv',
mimetype="Content-Type: text/html; charset={0}".format(ENCODING)
)
在前两种情况下,答案很简单:
您将流提供给send_file
,此函数将不会立即传输文件,而是将流包装并将其返回给Flask以供将来处理。您的pdfStuff
函数将在Flask开始处理您的流之前全部返回,并且在两种情况下(with
和finally
),该流将之前关闭返回。
第三种情况比较棘手(但是这个答案向我指出了正确的方向:Why is TextIOWrapper closing the given BytesIO stream?)。以与上述相同的方式,仅在bi
返回之后才处理build_csv
。因此tw
已被抛弃到垃圾收集器。当收集器销毁它时,tw
将隐式关闭bi
。解决这个问题的方法是在返回之前tw.detach()
(这将阻止TextIOWrapper
影响流)。
旁注(如果我记错了,请纠正我):
这种行为是有限制的,除非为send_file
提供了类似文件的对象时,它将自行处理关闭。从文档(https://flask.palletsprojects.com/en/0.12.x/api/#flask.send_file)中不清楚是否处理了关闭。我假设是这样(源代码中存在一些.close()
,而send_file
使用的werkzeug.wsgi.FileWrapper
也已经实现了.close()
),在这种情况下,您的方法可以纠正为:
@app.route('/pdf')
def pdfStuff():
return send_file(open('pdffile.pdf','rb'), attachment_filename='pdffile.pdf')
在这种情况下,当然会提供文件名。但是在其他情况下,可能需要将文件流包装在某些操作管道中(解码/压缩)