0

I'm using Quart (Flask fork) and I have a view which handles ".bin" file upload (100MB). But on file.save(path) call I can see the file appears in destination folder for moment and immediately removed. Can't get why it's happening.

View code:

@app.post("/settings/system-update")
async def system_update():
    files = await request.files
    update_img = files.get("update-img")

    if update_img is None or update_img.filename == '':
        logger.error(f"Update Image was not found in files: {files}")
        await flash("Update Image was not found in uploaded files", "error")
        return redirect(url_for("settings"))

    # Create temporary directory if not exists
    update_tmp_folder = os.path.join(config.TMP_DIR, "update")
    os.makedirs(update_tmp_folder, exist_ok=True)

    image_name = secure_filename(update_img.filename)
    file_path = os.path.join(update_tmp_folder, image_name)
    
    await update_img.save(file_path)    
    
    return redirect(url_for("settings"))

file_path returns correct path string.

I have @app.after_serving which has clean up logic but the code lines related to temp folder clean up are commented out (I had idea that temp content might be removed on server reload) as well as switching over use_reloader=False.

File uploaded via AJAX request without <form> tag:

<div class="input-group">
    <input id="updateFileInput" class="form-control" type="file" name="update-img" data-update-url="{{ url_for('system_update') }}">
    <button id="updateApplyBtn" class="btn btn-success" type="button">Start updating</button>
</div>    

JS code:

// Update upload
    $updateApplyBtn.on("click", function () {
        var updateFile = $updateFileInput[0].files[0];
        var formData = new FormData();
        formData.append("update-img", updateFile);

        $.ajax({
            url: $updateFileInput.data("update-url"),
            type: "POST",
            data: formData,
            contentType: false,
            processData: false,
            success: function (res) {
                console.log(res)
            }
        });
    });

1 Answer 1

0

Unfortunately, I was unable to reproduce the behavior you described. Furthermore, it's still unclear to me why you're using an AJAX approach instead of sending the form directly to the server.

In the following example, I've slightly modified your code, although the essential parts of the code for the endpoint are identical to yours. Files are uploaded as expected and appear in a subdirectory of the instance folder.

from quart import Quart
from quart import (
    redirect, 
    render_template, 
    request, 
    url_for
)
from werkzeug.utils import secure_filename
import os

app = Quart(__name__)
app.config.from_mapping(
    TMP_DIR=os.path.join(app.instance_path, 'uploads')
)

@app.route('/')
async def index():
    return await render_template('index.html', **locals())

@app.post('/settings/system-update')
async def system_update():
    files = await request.files;
    file = files.get('update-img')

    if file is None or file.filename == '':
        return redirect(url_for('index'))

    upload_folder = os.path.join(app.config['TMP_DIR'], 'update')
    os.makedirs(upload_folder, exist_ok=True)

    filename = secure_filename(file.filename)
    filepath = os.path.join(upload_folder, filename)

    await file.save(filepath)

    return redirect(url_for('index'))
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Index</title>
</head>
<body>

    <form name="upload-form" action="{{ url_for('system_update') }}">
        <input type="file" name="update-img" />
        <button type="submit">Start updating</button>
    </form>

    <script 
        src="https://code.jquery.com/jquery-3.7.1.min.js" 
        integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" 
        crossorigin="anonymous"></script>
    <script>
        (function() {
            $('form[name="upload-form"]').on('submit', function(evt) {
                evt.preventDefault();
                $.ajax({
                    url: $(this).attr('action'), 
                    type: 'POST', 
                    data: new FormData(this), 
                    contentType: false, 
                    processData: false, 
                    success: console.log
                });
            });
        })();
    </script>

</body>
</html>
Sign up to request clarification or add additional context in comments.

1 Comment

Replying to your question: it's still unclear to me why you're using an AJAX approach instead of sending the form directly to the server. Due to template design I already have a "global" form which is a wrapper for bootstrap tab's content where User able to change the settings. And as update input is a part of System settings but has to be handled individually (has individual Update button once file attached) I had to keep a separate POST Update view for this logic. I've create another folder in the project's root (same level as temp folder) and file saves there successfully.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.