Python / Flask - ValueError:关闭文件的I / O操作

时间:2016-05-01 12:14:12

标签: python python-2.7 pdf flask

在有人说这是重复之前,我认为这不是因为我看过类似的问题并且他们没有帮助我!

我在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(...)并使用tryfinally语句,如下所示:

static_file = open('pdffile.pdf','rb')
try:
        return send_file(static_file, attachment_filename='pdffile.pdf')
finally:
        static_file.close()

上面的代码发生了同样的错误,我不明白为什么。有谁知道我可能做错了什么?

很抱歉,如果我是愚蠢的,并且有一些简单的事情我犯了错误!

非常感谢你!!

2 个答案:

答案 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开始处理您的流之前全部返回,并且在两种情况下(withfinally),该流将之前关闭返回。

第三种情况比较棘手(但是这个答案向我指出了正确的方向: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')

在这种情况下,当然会提供文件名。但是在其他情况下,可能需要将文件流包装在某些操作管道中(解码/压缩)