【问题标题】:bottle.py raising ValueError('I/O operation on closed file',) with file.save()bottle.py 用 file.save() 提高 ValueError('I/O operation on closed file',)
【发布时间】:2019-12-17 01:59:26
【问题描述】:

对于我当前的项目,我正在使用 python 瓶。目前,我正在尝试保存用户在表单中上传的文件,但它会引发上面显示的错误。我尽我所能遵循文档,但无论我尝试过什么,它都会出现此错误。

函数如下:

def uploadFile(file, isPublic, userId, cu, dirId=-1):
    """Takes a bottle FileUpload instance as an argument and adds it to storage/media as well as adds its 
    info to the database. cu should be the db cursor. isPublic should be a bool. userId should be the 
    uploaders id. dirId is the directory ID and defaults to -1."""
    fakeName = file.filename
    extension = os.path.splitext(file.filename)[1]
    cu.execute("SELECT * FROM files WHERE fileId=(SELECT MAX(fileId) FROM files)")
    newId = cu.fetchone()
    if newId==None:
        newId = 0
    else:
        newId = newId[1]
    if debugMode:
        print(f"newId {newId}") 
    fileName = f"userfile-{newId}-{userId}.{extension}"
    file.save(vdata["m_folder"] + "/" + fileName)
    cu.execute("INSERT INTO files VALUES (?, ?, ?, ?, ?, ?)",
    (userId, newId, dirId, fakeName, fileName, isPublic))
    cu.connection.commit()

有谁知道可能是什么问题?

【问题讨论】:

  • 正如错误“对已关闭文件的 I/O 操作”中所述,您正在尝试使用已关闭的文件对象。使用 with 语句,打开文件对象然后关闭它。
  • 变量和函数名应该遵循lower_case_with_underscores风格。
  • @AlexanderCécile 你确定吗?我发现python标准库有一半时间没有遵循这个约定。
  • @AlexanderCécile 很抱歉,我目前没有任何想法,但我记得通读文档并调整我在这里使用的样式,因为我发现它是最容易阅读,但我会记住在我的下一个项目中使用lower_case_with_underscores
  • @AlexanderCécile file 参数是一个 bottle.py FileUpload 对象;似乎错误在于 FileUpload 对象的内部文件对象在调用 file.save 时尚未打开。

标签: python python-3.x bottle


【解决方案1】:

使用 Bottle 框架上传文件的示例

我不确定您是否需要这个答案,但我想将它包含在未来的读者中。下面是如何使用bottle框架上传文件的完整示例

目录结构:

.
├── app.py
├── uploaded_files
└── views
    └── file_upload.tpl

app.py:

import os
from bottle import Bottle, run, request, template

app = Bottle()

def handle_file_upload(upload):
    name, ext = os.path.splitext(upload.filename)
    if ext not in ('.png','.jpg','.jpeg'):
        return 'File extension not allowed.'

    save_path = "uploaded_files"
    upload.save(save_path) # appends upload.filename automatically
    return 'OK'

@app.route('/')
def file_upload():
    return template('file_upload')

@app.route('/upload', method='POST')
def do_upload():
    category   = request.forms.get('category')
    upload     = request.files.get('upload')
    return handle_file_upload(upload)

run(app, host='localhost', port=8080, debug=True)

views/file_upload.tpl:

<form action="/upload" method="post" enctype="multipart/form-data">
  Category:      <input type="text" name="category" />
  Select a file: <input type="file" name="upload" />
  <input type="submit" value="Start upload" />
</form>

输出:

上传有效文件后的截图:

上述案例中可能存在的问题:

以下代码块有可能引发异常:

fileName = f"userfile-{newId}-{userId}.{extension}"
file.save(vdata["m_folder"] + "/" + fileName)

请检查每个变量的值:newIduserIdextensionvdata["m_folder"]

您也可以将"/" 替换为os.sepos.path.sep

正如@AlexanderCécile 在评论中提到的,将文件对象传递给外部方法也可能导致问题。您可以将变量名称 file 重命名为其他名称,但我认为它根本无法解决问题。

更新

我已经更新了代码。现在我将文件对象发送到另一个方法而不是路由函数,但代码仍然可以正常工作。

参考:

  1. bottle official docs for file uploading

【讨论】:

  • 这是一个非常好的答案,我很感激;但是,我想指出,从我的路由函数中,我调用了一个单独的函数(如我的问题所示)。
  • @Brendon,我已经更新了答案。现在我正在一个名为handle_file_upload 的单独函数中处理文件上传部分。我仍然可以上传新文件。将文件对象传递给单独的函数时没有问题。
【解决方案2】:

您似乎还没有打开要写入的文件。你可以这样做:

with open('path/to/file', 'w') as file:
    file.write('whatever')

【讨论】:

  • 你的意思是我也应该在文件上写吗? (即不是瓶子文件对象,而是目标文件)
  • 我没有使用过瓶子框架,所以我无法评论这方面。但是正如您看到的错误所示,目标文件需要打开(具有写入权限)。
  • 谢谢,我认为这是正确的答案。没有意识到这一点,我真是个笨蛋。
  • 好吧,问题仍然存在。看来这不是问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 2015-11-27
  • 1970-01-01
  • 1970-01-01
  • 2021-05-19
  • 2021-03-02
  • 1970-01-01
相关资源
最近更新 更多