TinyMCE and Flask - Part 2 - Managing Images
Posted on
by Kevin FoongThis is the second part of my earlier Flask / TinyMCE tutorial. See the first part here.
In this blog post I will outline the steps I used to save a thumbnail version of any image I upload to my post. Here I used the very useful Pillow (Python Imaging Library) library.
TinyMCE has a great image uploader feature where you can simply drag and drop images to directly upload into your post. I have summarised some of the main implementation points below:
- The
imageuploader
function handles the image upload process. - We obtain the image itself via
file = request.files.get('file')
- We truncate the filename to 30 characters.
- We only save the file if the image is a jpg, gif or png file by checking its file extension.
- We obtain the actual file size using
os.stat(img_fullpath).st_size
- We use the Pillow library to get the image dimensions.
- We use the Pillow to make a thumbnail copy of the image and save it in a separate thumbnail folder.
- We also save all details into a MySQL database.
The full source code is shown below.
from PIL import Image
# Handles javascript image uploads from tinyMCE
@bp.route('/imageuploader', methods=['POST'])
@login_required
def imageuploader():
file = request.files.get('file')
if file:
filename = file.filename.lower()
fn, ext = filename.split('.')
# truncate filename (excluding extension) to 30 characters
fn = fn[:30]
filename = fn + '.' + ext
if ext in ['jpg', 'gif', 'png', 'jpeg']:
try:
# everything looks good, save file
img_fullpath = os.path.join(current_app.config['UPLOADED_PATH'], filename)
file.save(img_fullpath)
# get the file size to save to db
file_size = os.stat(img_fullpath).st_size
size = 160, 160
# read image into pillow
im = Image.open(img_fullpath)
# get image dimension to save to db
file_width, file_height = im.size
# convert to thumbnail
im.thumbnail(size)
thumbnail = fn + '-thumb.jpg'
tmb_fullpath = os.path.join(current_app.config['UPLOADED_PATH_THUMB'], thumbnail)
# PNG is index while JPG needs RGB
if not im.mode == 'RGB':
im = im.convert('RGB')
# save thumbnail
im.save(tmb_fullpath, "JPEG")
# save to db
img = Images(filename=filename, thumbnail=thumbnail, file_size=file_size, \
file_width=file_width, file_height=file_height)
db.session.add(img)
db.session.commit()
except IOError:
return make_response('Cannot create thumbnail for ' + filename, 500)
return jsonify({'location' : filename})
# fail, image did not upload
return make_response('Filename needs to be JPG, JPEG, GIF or PNG', 500)
You can also create an admin page to manage all the images, as I have done below. This page also has the ability to delete images which will delete the actual image file, its thumbnail and the database record.
CSRF token issue
Please note that if you have CSRF enabled globally throughout your site the imageuploader won't work because tinymce won't be sending the CSRF token to Flask. The easiest way to get around this would be to exempt CSRF token checking on the route using the csrf.exempt
decorator.
...
@bp.route('/page/imageuploader', methods=['POST'])
@login_required
@csrf.exempt
def imageuploader():
...
The suggestion by the TinyMCE community at time of writing is to use the images_upload_handler
option instead of images_upload_url
- which we are not doing. See more information here.
See the full source code on Github.
Let me know if you have any feedback!