1

I am working with Flask, WTForms and Flask-WTF to save a file but the file is always store as an empty file. It doesn't show any error.

Form:

from flask_wtf import FlaskForm
from flask_wtf.file import FileField
from wtforms import StringField, SubmitField

class UserForm(FlaskForm):
    username = StringField('u', validators=[InputRequired()])
    email = StringField('e', validators=[InputRequired()])
    password = StringField('c', validators=[InputRequired()])
    signature = FileField('s')
    save = SubmitField('a')

Flask Route / Function:

@bp.route('/manage/user', methods=['GET', 'POST'])
@login_required
def manage_user():
    form = UserForm()
    action = 'create'

    try:        
        if form.validate_on_submit():
            if form.save.data:
                print('request.method', request.method)
                print('request.args', request.args)
                print('request.form', request.form)
                print('request.files', request.files)
                # archivo = request.files['signature']
                # archivo = request.files.items()[0]
                archivo = form.signature.data
                print(type(archivo))
                if archivo:
                    image_data = archivo.read()
                    basedir = os.path.abspath(os.path.dirname(__file__))
                    filename = secure_filename(form.username.data+'.png')
                    full_dir = os.path.join(basedir, '..\\static\\img', filename)
                    print(sys.getsizeof(archivo))
                    archivo.save(full_dir) 
                return redirect(url_for('main.manage'))
    except Exception as e:
        print(e)

    return render_template('manage_user.html', title=('Admon'), form=form, action=action)

HTML

<form action="" method="POST" role="form" class="form-horizontal" enctype="multipart/form-data">
    {{ form.csrf_token() }}
    <div class="form-group">
        <label class="control-label col-sm-3">Firma</label>
        <div class="col-sm-6">
            {% if action=='create' %}
            <!-- Other fields -->
            {{ form.signature(placeholder="Firma", type="file", class="form-control", accept="image/*", required="True") }}
            <img id="firmaimg" src="{{ url_for('static', filename='img/firma130x50_dummy.png') }}" alt="Firma" />
            {{ form.save(class="btn btn-success btn-md") }}
            {% endif %}
        </div>
    </div>
</form>

The print statements show this: Print results

The order of the prints is:

  1. Request method
  2. Request Arguments
  3. Request Form
  4. Request Files
  5. Type of archivo variable

Size of archivo is always 32, it doesn't matter what image I upload, is always 32, so from that point is already empty, haven't figure out why is coming empty. I'm following the examples.

https://flask-wtf.readthedocs.io/en/latest/form.html

Rednaxel
  • 938
  • 2
  • 16
  • 33

2 Answers2

4

For anyone who has just stumbled upon this,

This error occurs when you perform save() and read() on the same file object.

I extended my research by checking 2 scenarios:

1.read() before save()

read() function will read the file and place the cursor at end of file. If this object is now saved, it will be empty.

2. save() before read()

file object becomes empty after you successfully save() the file. If you try to read it now, you will get an empty string ''

3. Deepcopy this file object so as to perform read() and save() on separate file objects

Not allowed. Some file types (such as FileStorage) cannot be deep copied. Refer this answer

Workaround

As per Naisarg's comment in this answer:

  1. save file in backend.

    file = request.files['file']

    file.save(secure_filename(file.filename))

  2. Read that file.

  3. Attached that file to 3rd party API.

    r = requests.post(url, headers=headers, data=payload, files={'file': (file.filename, open(file.filename, 'rb'), file.content_type)})

  4. Delete file in backend.

    os.remove(file.filename)

3

Firstly, the size of your file is always 32 because sys.getsizeof() returns the amount of memory in bytes Python uses for that object, as explained in the docs.

To print the actual file size you can use:

os.path.getsize('path')

Using it, you should see that the printed file size is effectively 0.

Secondly, by removing:

image_data = archivio.read()

you should be able to successfully save the file in the desired location.

I'm not sure what you are trying to accomplish with archivio.read().

azds
  • 92
  • 8
  • ok, for the first part, the size, does that give me the size of the bytes object? Or is it the size of the element in the local directory? – Rednaxel Jan 03 '19 at 06:03
  • The second part, Thank you SO much. I was just testing a lot of things and that always stayed there. You saved my life. – Rednaxel Jan 03 '19 at 06:04