Search code examples
pythonflaskwerkzeug

Flask redirect with uploaded file - I/O operation on closed file


This app should allow a user to upload a file and then depending on the file type it would perform a save task. If it's a PDF file a new selection page loads prompting the user to select a folder. Once selected the error: ValueError: I/O operation on closed file pops up and an empty PDF file is saved in the selected location.

".mxd" files process with no issues. It seems to be because I have redirected to the selection template, but I'm not sure how else I would be able to use the folder selection.

Much code has been omitted to keep things simple. Any help would be greatly appreciated.

@app.route("/", methods=['GET', 'POST'])  
def upload_file(): 
form = ReusableForm(request.form)  # calls on form

if request.method == 'POST':
    global folderout
    folderout = request.form['folderout']

    global file
    file = request.files['file']
        if filename.endswith((".pdf")):  # For PDF files only
            return redirect("/selection")
            return redirect("/editor")

        if filename.endswith((".mxd")):
            MXDfull.savemxd()
            MXDfull.pdf()
            MXDfull.thumb()
            return redirect("/editor")

@app.route("/selection", methods=['GET', 'POST'])
def selection1():
    form = SelectionForm(request.form)
    if request.method == 'POST':
        global selection
        selection = request.form['selection']
        pdffilesave.savepdf()

    return render_template("selection.html", form=form)

class PDFFile:
    def savepdf(self):
            self.pdffolder = os.path.join(folderout,selection)
            self.pdffilename = "K" + Fnum + ".pdf"
            file.save(os.path.join(self.pdffolder, self.pdffilename))
            return
    pdffilesave = PDFFile()

Page 1:

Upload file

Page 2:

enter image description here

Page 3:

enter image description here


Solution

  • Flask creates a FileStorage object which is a thin wrapper over incoming files.

    The stream attribute of this object usually points to an open temporary file (according to the documentation). I'm guessing that as soon as the request is served, this temporary file is closed and hence the reference to this stream from your global object file points to a closed file. You must have got this error ValueError: I/O operation on closed file.

    One work around would be to save the file in upload_file method in a temporary location and store the location of this file in a global variable filename.

    @app.route("/", methods=['GET', 'POST'])  
    def upload_file(): 
    ....
    file = request.files['file']
    global file_name = '/tmp/' + file.filename
    
    file.save(file_name)
    if file.filename.endswith((".pdf")):  # For PDF files only
        return redirect("/selection")
        return redirect("/editor")
    ...
    

    In the selection method, you can move the file from temporary location to the desired location.

    @app.route("/selection", methods=['GET', 'POST'])
    def selection1():
    ....
    os.rename(file_name, dest_file_name)
    ...