or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

csrf-protection.mdfile-upload.mdform-handling.mdindex.mdrecaptcha.md

file-upload.mddocs/

0

# File Upload

1

2

Secure file upload handling with Werkzeug integration, providing specialized file fields and comprehensive validation including file type restrictions, size limits, and presence checking.

3

4

## Capabilities

5

6

### File Fields

7

8

Werkzeug-aware file upload fields that properly handle FileStorage objects from Flask requests.

9

10

```python { .api }

11

class FileField:

12

"""

13

Werkzeug-aware file upload field.

14

Processes FileStorage objects from Flask request.files.

15

"""

16

def process_formdata(self, valuelist):

17

"""Process form data containing FileStorage objects"""

18

19

class MultipleFileField:

20

"""

21

Multiple file upload field supporting multiple FileStorage objects.

22

23

Added in version 1.2.0

24

"""

25

def process_formdata(self, valuelist):

26

"""Process form data containing multiple FileStorage objects"""

27

```

28

29

### File Validators

30

31

Comprehensive validation for uploaded files including presence, type restrictions, and size limits.

32

33

```python { .api }

34

class FileRequired:

35

"""

36

Validates that uploaded file(s) are present and valid FileStorage objects.

37

38

Args:

39

message: Custom error message

40

"""

41

def __init__(self, message=None): ...

42

def __call__(self, form, field): ...

43

44

class FileAllowed:

45

"""

46

Validates that uploaded file(s) have allowed extensions.

47

48

Args:

49

upload_set: List of allowed extensions or Flask-Uploads UploadSet

50

message: Custom error message

51

"""

52

def __init__(self, upload_set, message=None): ...

53

def __call__(self, form, field): ...

54

55

class FileSize:

56

"""

57

Validates that uploaded file(s) are within size limits.

58

59

Args:

60

max_size: Maximum file size in bytes

61

min_size: Minimum file size in bytes (default: 0)

62

message: Custom error message

63

"""

64

def __init__(self, max_size, min_size=0, message=None): ...

65

def __call__(self, form, field): ...

66

```

67

68

### Validator Aliases

69

70

Convenient lowercase aliases for file validators.

71

72

```python { .api }

73

file_required = FileRequired

74

file_allowed = FileAllowed

75

file_size = FileSize

76

```

77

78

## Usage Examples

79

80

### Basic File Upload

81

82

```python

83

from flask_wtf import FlaskForm

84

from flask_wtf.file import FileField, FileRequired, FileAllowed

85

from wtforms import StringField, SubmitField

86

from wtforms.validators import DataRequired

87

88

class UploadForm(FlaskForm):

89

title = StringField('Title', validators=[DataRequired()])

90

file = FileField('File', validators=[

91

FileRequired(),

92

FileAllowed(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'], 'Images and documents only!')

93

])

94

submit = SubmitField('Upload')

95

96

@app.route('/upload', methods=['GET', 'POST'])

97

def upload_file():

98

form = UploadForm()

99

100

if form.validate_on_submit():

101

title = form.title.data

102

file = form.file.data # Werkzeug FileStorage object

103

104

if file:

105

filename = secure_filename(file.filename)

106

file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))

107

flash(f'File {filename} uploaded successfully!')

108

return redirect(url_for('upload_file'))

109

110

return render_template('upload.html', form=form)

111

```

112

113

### Multiple File Upload

114

115

```python

116

from flask_wtf.file import MultipleFileField, FileAllowed, FileSize

117

118

class MultiUploadForm(FlaskForm):

119

files = MultipleFileField('Files', validators=[

120

FileAllowed(['jpg', 'png', 'gif'], 'Images only!'),

121

FileSize(max_size=5*1024*1024, message='Files must be smaller than 5MB') # 5MB limit

122

])

123

submit = SubmitField('Upload All')

124

125

@app.route('/multi-upload', methods=['GET', 'POST'])

126

def multi_upload():

127

form = MultiUploadForm()

128

129

if form.validate_on_submit():

130

files = form.files.data # List of FileStorage objects

131

132

uploaded_files = []

133

for file in files:

134

if file:

135

filename = secure_filename(file.filename)

136

file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))

137

uploaded_files.append(filename)

138

139

flash(f'Uploaded {len(uploaded_files)} files: {", ".join(uploaded_files)}')

140

return redirect(url_for('multi_upload'))

141

142

return render_template('multi_upload.html', form=form)

143

```

144

145

### Advanced File Validation

146

147

```python

148

class AdvancedUploadForm(FlaskForm):

149

# Required file with extension and size validation

150

document = FileField('Document', validators=[

151

FileRequired('Please select a file'),

152

FileAllowed(['pdf', 'doc', 'docx'], 'Documents only'),

153

FileSize(max_size=10*1024*1024, min_size=1024,

154

message='File must be between 1KB and 10MB')

155

])

156

157

# Optional image with validation

158

image = FileField('Image', validators=[

159

FileAllowed(['jpg', 'jpeg', 'png', 'gif'], 'Images only'),

160

FileSize(max_size=2*1024*1024, message='Image must be smaller than 2MB')

161

])

162

163

submit = SubmitField('Upload')

164

165

# Using validator aliases

166

class SimpleUploadForm(FlaskForm):

167

file = FileField('File', validators=[

168

file_required('File is required'),

169

file_allowed(['txt', 'csv'], 'Text files only'),

170

file_size(max_size=1024*1024, message='File too large') # 1MB

171

])

172

```

173

174

### Flask-Uploads Integration

175

176

Flask-WTF integrates with Flask-Uploads for organized file handling:

177

178

```python

179

from flask_uploads import UploadSet, configure_uploads, IMAGES, DOCUMENTS

180

181

# Configure upload sets

182

photos = UploadSet('photos', IMAGES)

183

documents = UploadSet('documents', DOCUMENTS)

184

configure_uploads(app, (photos, documents))

185

186

class UploadsForm(FlaskForm):

187

# Using UploadSet with FileAllowed

188

photo = FileField('Photo', validators=[

189

FileRequired(),

190

FileAllowed(photos, 'Images only!') # Uses UploadSet

191

])

192

193

document = FileField('Document', validators=[

194

FileAllowed(documents, 'Documents only!') # Uses UploadSet

195

])

196

197

submit = SubmitField('Upload')

198

```

199

200

### Custom File Validation

201

202

```python

203

from wtforms import ValidationError

204

import imghdr

205

206

class ImageUploadForm(FlaskForm):

207

image = FileField('Image', validators=[FileRequired()])

208

209

def validate_image(self, field):

210

"""Custom validator to check if file is a valid image"""

211

if field.data:

212

# Check file header to verify it's actually an image

213

field.data.seek(0) # Reset file pointer

214

if not imghdr.what(field.data):

215

raise ValidationError('File is not a valid image')

216

field.data.seek(0) # Reset for saving

217

218

# Check image dimensions

219

from PIL import Image

220

try:

221

img = Image.open(field.data)

222

width, height = img.size

223

if width > 2000 or height > 2000:

224

raise ValidationError('Image dimensions too large (max 2000x2000)')

225

field.data.seek(0) # Reset for saving

226

except Exception:

227

raise ValidationError('Cannot process image file')

228

```

229

230

### File Processing Examples

231

232

```python

233

import os

234

from werkzeug.utils import secure_filename

235

236

@app.route('/process-upload', methods=['GET', 'POST'])

237

def process_upload():

238

form = UploadForm()

239

240

if form.validate_on_submit():

241

file = form.file.data

242

243

# File information

244

filename = file.filename

245

content_type = file.content_type

246

content_length = file.content_length

247

248

# Secure the filename

249

safe_filename = secure_filename(filename)

250

251

# Create unique filename to prevent conflicts

252

timestamp = int(time.time())

253

unique_filename = f"{timestamp}_{safe_filename}"

254

255

# Save file

256

file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename)

257

file.save(file_path)

258

259

# Process file content

260

if content_type.startswith('text/'):

261

with open(file_path, 'r', encoding='utf-8') as f:

262

content = f.read()

263

# Process text content

264

elif content_type.startswith('image/'):

265

from PIL import Image

266

img = Image.open(file_path)

267

# Process image

268

269

return {'status': 'success', 'filename': unique_filename}

270

271

return render_template('upload.html', form=form)

272

```

273

274

## Template Examples

275

276

### Single File Upload Template

277

278

```html

279

<form method="POST" enctype="multipart/form-data">

280

{{ form.hidden_tag() }}

281

282

<div class="form-group">

283

{{ form.title.label(class="form-label") }}

284

{{ form.title(class="form-control") }}

285

{% for error in form.title.errors %}

286

<div class="text-danger">{{ error }}</div>

287

{% endfor %}

288

</div>

289

290

<div class="form-group">

291

{{ form.file.label(class="form-label") }}

292

{{ form.file(class="form-control", accept=".txt,.pdf,.png,.jpg,.jpeg,.gif") }}

293

{% for error in form.file.errors %}

294

<div class="text-danger">{{ error }}</div>

295

{% endfor %}

296

</div>

297

298

{{ form.submit(class="btn btn-primary") }}

299

</form>

300

```

301

302

### Multiple File Upload Template

303

304

```html

305

<form method="POST" enctype="multipart/form-data">

306

{{ form.hidden_tag() }}

307

308

<div class="form-group">

309

{{ form.files.label(class="form-label") }}

310

{{ form.files(class="form-control", multiple=true, accept="image/*") }}

311

<small class="form-text text-muted">

312

Select multiple image files (max 5MB each)

313

</small>

314

{% for error in form.files.errors %}

315

<div class="text-danger">{{ error }}</div>

316

{% endfor %}

317

</div>

318

319

{{ form.submit(class="btn btn-primary") }}

320

</form>

321

```

322

323

## Configuration

324

325

### Upload Settings

326

327

```python

328

import os

329

330

# Basic upload configuration

331

app.config['UPLOAD_FOLDER'] = 'uploads'

332

app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max request size

333

334

# Ensure upload directory exists

335

os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

336

```

337

338

### Security Considerations

339

340

1. **Always use `secure_filename()`** to sanitize uploaded filenames

341

2. **Validate file types** using both extension and content checking

342

3. **Set reasonable size limits** to prevent resource exhaustion

343

4. **Store uploads outside the web root** when possible

344

5. **Scan uploads for viruses** in production environments

345

6. **Use unique filenames** to prevent conflicts and enumeration