Brendon
Brendon

Reputation: 33

bottle.py raising ValueError('I/O operation on closed file',) with file.save()

For my current project, I am using python bottle. At the moment, I am trying to save a file that the user uploads in a form, but it's raising the error shown above. I followed the docs in the best way I could, but no matter what I have tried it gave this error.

Here is the function:

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()

Does anyone know what the problem might be?

Upvotes: 1

Views: 179

Answers (2)

arshovon
arshovon

Reputation: 13661

Example of uploading files using Bottle framework

I am not sure if you need this answer anymore but I like to include it for future readers. Here is a complete example of How to upload file using bottle framework

Directory structure:

.
├── 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>

Output:

Screenshot after uploading a valid file:

screenshot after uploading a valid file

Probable problem in the mentioned case:

There is a chance that the following code block may raise the exception:

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

Please check the values of each variables: newId, userId, extension, vdata["m_folder"]

Also you can replace "/" with os.sep or os.path.sep.

As @AlexanderCécile mentioned in comment, passing the file object to external method may also cause the problem. You may rename the variable name file to something else, but I don't think it will fix the problem at all.

Update

I have updated the code. Now I am sending the file object to another method rather than the routing function but the code still works perfectly.

Reference:

  1. bottle official docs for file uploading

Upvotes: 1

Oliver Lorton
Oliver Lorton

Reputation: 719

It looks like you've not opened the file for writing yet. You can do that like this:

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

Upvotes: 1

Related Questions