Archivos en formulario de Flask
Creación de formulario
En este artículo veremos cómo manejar la subida de archivos desde formularios en el framework Flask, cómo guardarlos y cómo limitar el tipo y tamaño de archivos permitidos a través de las validaciones de campo.
Para permitir la subida de archivos en nuestra aplicación Flask utilizaremos la extensión Flask-WTF que permite la creación y manejo de formularios a través de clases. En este ejemplo crearemos un formulario que tenga solamente un campo para la carga de un archivo y haremos que ese archivo se almacene en un directorio al ser enviado el formulario.
Empezaremos por crear la clase de formulario, esta tendrá definido cada campo del formulario y las validaciones para cada uno de ellos. Para especificar un campo del tipo archivo se utiliza la clase FileField
. El primer argumento será la etiqueta del campo, a continuación se listan las validaciones requeridas para el campo. Los campos del tipo FileField
aceptan un tipo de validador FileRequired
que verifica que un archivo ha sido cargado en el campo.
Finalmente agregamos también un campo del tipo SubmitField
que representará el botón de envío del formulario.
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired
from wtforms import SubmitField
class ArchivoForm(FlaskForm):
archivo = FileField('Selecciona un archivo', validators=[FileRequired()])
submit = SubmitField('Subir')
A continuación realizaremos el template donde se mostrará el formulario. En este ejemplo solo se muestra la parte del archivo que involucra el formulario pero para una mejor visualización es recomendable hacer uso de todos los recursos de Jinja como la herencia de templates y macros.
La etiqueta de formulario debe especificar el tipo de protocolo de envío (GET o POST) y la url a la que se realizará dicho envío. Cuando un formulario debe ser capaz de enviar archivos es necesario especificar también enctype="multipart/form-data"
ya que de caso contrario el campo enviará un valor vacío.
Se muestra la etiqueta del campo de archivo form.archivo.label
y el campo de subida con form.archivo
. El botón de envío es renderizado al final del formulario con form.submit
.
Para activar la protección CSRF en el formulario debe crearse adicionalmente un campo oculto con el nombre csrf_token
cuyo valor debe está dado por la llamada a la función csrf_token()
.
<form method="POST" action="{{ url_for('subir') }}" enctype="multipart/form-data" >
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
{{ form.archivo.label }}
{{ form.archivo }}
{{ form.submit }}
</form>
Esto dará como resultado un formulario con un campo que permite la carga de archivos y un botón de envío.
Para poder utilizar la protección CSRF es necesario inicializar la clase CSRFProtect
al crear la aplicación Flask. Esta clase requiere que se configure la clave secreta SECRET_KEY
ya que está será utilizada como parte del cálculo del csrf_token
.
En este ejemplo también se ha creado una configuración DIRECTORIO
que será usada por la aplicación para determinar la ubicación de los archivos al guardarlos.
import os
from flask import Flask
from flask_wtf import CSRFProtect
csrf = CSRFProtect()
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "clave-secreta"
app.config["DIRECTORIO"] = "/tmp"
csrf.init_app(app)
return app
Dentro de la ruta de nuestro formulario debemos inicializar la clase del formulario. Este objeto será enviado al template para renderizarlo. El método validate_on_submit
verificará si el formulario ha sido enviado y que las condiciones de validación se hayan cumplido correctamente.
Antes de almacenar el archivo es altamente recomendable aplicar primero la función secure_filename
de la librería de seguridad werkzeug
. Esta función se encargará de modificar el nombre del archivo por uno seguro evitando de esta manera que un atacante pueda ingresar un nombre de archivo que apunte a un directorio del sistema. El nombre del archivo enviado puede obtenerse en el atributo filename
de los datos del campo.
Finalmente el archivo puede guardarse utilizando el método save
al cual le pasaremos como parámetro la ubicación obtenida de el directorio previamente configurado y el nombre seguro de archivo generado.
from flask import render_template, redirect, url_for, current_app
from ..forms.archivo_form import ArchivoForm
from werkzeug.utils import secure_filename
import os
@app.route('/subir', methods=["POST","GET"])
def subir():
form = ArchivoForm()
if form.validate_on_submit():
f = form.archivo.data
filename = secure_filename(f.filename)
f.save(os.path.join(
current_app.config['DIRECTORIO'], filename
))
return redirect(url_for('index'))
return render_template('archivo_form.html', form = form)
Validar tipos de archivos
Para restringir el tipo de archivo permitido dentro del campo de nuestro formulario se utiliza el validador FileAllowed
. Este permitirá especificar en forma de listo las extensiones de archivo aceptadas.
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileRequired, FileAllowed
from wtforms import SubmitField
class ArchivoForm(FlaskForm):
archivo = FileField('Selecciona un archivo',
validators= [FileRequired(),
FileAllowed(['jpg','jpeg', 'png'],'Formato no permitido')])
submit = SubmitField('Subir')
Validar tamaño de archivos
Si se quiere limitar el tamaño de los archivos subidos debe configurarse MAX_CONTENT_LENGTH
en la aplicación. Esto restringirá cualquier subida cuyo tamaño exceda el configurado. En el siguiente ejemplo se limita el tamaño a 10MB.
import os
from flask import Flask
from flask_wtf import CSRFProtect
csrf = CSRFProtect()
def create_app():
app = Flask(__name__)
app.config["SECRET_KEY"] = "clave-secreta"
app.config["DIRECTORIO"] = "/tmp"
app.config['MAX_CONTENT_LENGTH'] = 1 * 1024 * 1024
csrf.init_app(app)
return app
Cuando el tamaño es excedido Flask generará un error 413 y mostrará una pantalla de error por defecto. Para cambiar este comportamiento es necesario utilizar el decorador errorhandler
para definir la manera de manejar el error.
@app.errorhandler(413)
def exc_tamanio(e):
#Definir manejo de error
En este artículo hemos visto como utilizar los formularios de Flask para subir archivos a nuestro sistema. Hemos utilizado los tipos de validaciones más comunes para restringir el tipo y tamaño de los archivos como así también algunas técnicas de seguridad. Hasta la próxima!