Search code examples
flaskflask-wtforms

When uploading a file and using an secure_filename with Flask-WTF, I get the error TypeError: normalize() argument 2 must be str, not FileStorage


When trying to upload a file and using an secure_filename function with Flask-WTF, I get the error TypeError: normalize() argument 2 must be str, not FileStorage. How do I fix this? Does this have something todo with my Posts table code? Also did I add to much code below?

I think the error is caused by this line. filename_is_secure = secure_filename(picture_filename)

Here is the code

app.py


from flask import Flask, flash, render_template

from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField, TextAreaField, SubmitField
from wtforms.fields.html5 import EmailField
from wtforms import validators
from wtforms.fields.simple import FileField
from wtforms.validators import DataRequired, Length, ValidationError
from flask_wtf.file import FileField, FileRequired, FileAllowed
from flask_login import UserMixin 

from flask_sqlalchemy import SQLAlchemy
import uuid
import os 

app = Flask(__name__, template_folder='templates')
 

db = SQLAlchemy(app)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
app.config['SECRET_KEY'] = 'SomeKkey'

class FileForm(FlaskForm):

    filename = FileField('image', validators=
    [FileRequired() ,FileAllowed(['jpg', 'png'], 'Images only!')  ])
    submit = SubmitField('Submit')







app.config['UPLOAD_FOLDER'] = '\\path\\to\\the\\uploads'


class Posts(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key=True)
    profile_pic_name = db.Column(db.String())


from werkzeug.utils import secure_filename

@app.route('/', methods=['GET', 'POST'])
def home():

    form = FileForm()
    if form.validate_on_submit():
        picture_filename = form.filename.data

        # This makes sure the filename is safe
        filename_is_secure = secure_filename(picture_filename)
        # make the file unique incase someone uploads the same name
        # uuid is a random number generator
        unique_filename = str(uuid.uuid1()) + filename_is_secure
        posts = Posts(profile_pic_name=unique_filename)
        db.session.add(posts)
        db.session.commit()
        # save file to a secure location
        picture_filename.save(os.path.join(app.config['UPLOAD_FOLDER'], unique_filename))
    
        flash("You have succesfully uploaded your profile picture.")
    
    
    return render_template('home.html', form=form, title='upload Profile Picture') 


if __name__ == '__main__':
    app.run(debug=True)



templates/home.py

 



    <!DOCTYPE html>

    <html>


    <!--upload and update profile pics-->
    
    <head>
        <title> {{ title }}  </title>  
    </head>
    <body>
    
        <form action="" method="POST" enctype="multipart/form-data">
            {{ form.csrf_token }} 
            {{ form.filename }}
            <input type="submit" value="Submit">
        </form>
            
            
            
            
            <!--make flash message work-->
            {% with messages = get_flashed_messages() %}
                {% if messages %}
                    <ul class=flashes>
                    {% for message in messages %}
                        <p1>  {{message}} </p1>
                    {% endfor %}
                    </ul>
                {% endif %}
            {% endwith %}    
        
    </body>


    

    </html>
 

Here is the full error



Traceback (most recent call last):
  File "C:\Users\nmyle\anaconda3\envs\py\Lib\site-packages\flask\app.py", line 2091, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Users\nmyle\anaconda3\envs\py\Lib\site-packages\flask\app.py", line 2076, in wsgi_app
    response = self.handle_exception(e)
  File "C:\Users\nmyle\anaconda3\envs\py\Lib\site-packages\flask\app.py", line 2073, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\nmyle\anaconda3\envs\py\Lib\site-packages\flask\app.py", line 1518, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\nmyle\anaconda3\envs\py\Lib\site-packages\flask\app.py", line 1516, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\nmyle\anaconda3\envs\py\Lib\site-packages\flask\app.py", line 1502, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args)
  File "C:\Users\nmyle\OneDrive\Desktop\testing code folder todo delete\code folder\app.py", line 62, in home
    filename_is_secure = secure_filename(picture_filename)
  File "C:\Users\nmyle\anaconda3\envs\py\Lib\site-packages\werkzeug\utils.py", line 456, in secure_filename
    filename = unicodedata.normalize("NFKD", filename)
TypeError: normalize() argument 2 must be str, not FileStorage```

Solution

  • Your picture_filename variable is an instance of FileStorage, as this is what a WTForm FileField returns in its data attribute upon a successful upload.

    Function secure_filename takes a string type as its parameter, but you have passed a FileStorage instance, hence the error.

    Use the filename attribute of the FileStorage instance.

    picture_file_storage = form.filename.data
    filename_is_secure = secure_filename(picture_file_storage.filename)