views.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. from io import BytesIO
  2. from os import path
  3. from flask import current_app as app
  4. from flask import Blueprint
  5. from flask import flash
  6. from flask import redirect
  7. from flask import render_template
  8. from flask import url_for
  9. from s3_saver import S3Saver
  10. from project import db
  11. from library.prefix_file_utcnow import prefix_file_utcnow
  12. from foo.forms import ThingySaveForm
  13. from foo.models import Thingy
  14. mod = Blueprint('foo', __name__)
  15. @mod.route('/', methods=['GET', 'POST'])
  16. def home():
  17. """Displays the Flask S3 Save Example home page."""
  18. model = Thingy.query.first() or Thingy()
  19. form = ThingySaveForm(obj=model)
  20. if form.validate_on_submit():
  21. image_orig = model.image
  22. image_storage_type_orig = model.image_storage_type
  23. image_bucket_name_orig = model.image_storage_bucket_name
  24. # Initialise s3-saver.
  25. image_saver = S3Saver(
  26. storage_type=app.config['USE_S3'] and 's3' or None,
  27. bucket_name=app.config['S3_BUCKET_NAME'],
  28. access_key_id=app.config['AWS_ACCESS_KEY_ID'],
  29. access_key_secret=app.config['AWS_SECRET_ACCESS_KEY'],
  30. field_name='image',
  31. storage_type_field='image_storage_type',
  32. bucket_name_field='image_storage_bucket_name',
  33. base_path=app.config['UPLOADS_FOLDER'],
  34. static_root_parent=path.abspath(
  35. path.join(app.config['PROJECT_ROOT'], '..')))
  36. form.populate_obj(model)
  37. if form.image.data:
  38. filename = prefix_file_utcnow(model, form.image.data)
  39. filepath = path.abspath(
  40. path.join(
  41. path.join(
  42. app.config['UPLOADS_FOLDER'],
  43. app.config['THINGY_IMAGE_RELATIVE_PATH']),
  44. filename))
  45. # Best to pass in a BytesIO to S3Saver, containing the
  46. # contents of the file to save. A file from any source
  47. # (e.g. in a Flask form submission, a
  48. # werkzeug.datastructures.FileStorage object; or if
  49. # reading in a local file in a shell script, perhaps a
  50. # Python file object) can be easily converted to BytesIO.
  51. # This way, S3Saver isn't coupled to a Werkzeug POST
  52. # request or to anything else. It just wants the file.
  53. temp_file = BytesIO()
  54. form.image.data.save(temp_file)
  55. # Save the file. Depending on how S3Saver was initialised,
  56. # could get saved to local filesystem or to S3.
  57. image_saver.save(
  58. temp_file,
  59. app.config['THINGY_IMAGE_RELATIVE_PATH'] + filename,
  60. model)
  61. # If updating an existing image,
  62. # delete old original and thumbnails.
  63. if image_orig:
  64. if image_orig != model.image:
  65. filepath = path.join(
  66. app.config['UPLOADS_FOLDER'],
  67. image_orig)
  68. image_saver.delete(filepath,
  69. storage_type=image_storage_type_orig,
  70. bucket_name=image_bucket_name_orig)
  71. glob_filepath_split = path.splitext(path.join(
  72. app.config['MEDIA_THUMBNAIL_FOLDER'],
  73. image_orig))
  74. glob_filepath = glob_filepath_split[0]
  75. glob_matches = image_saver.find_by_path(
  76. glob_filepath,
  77. storage_type=image_storage_type_orig,
  78. bucket_name=image_bucket_name_orig)
  79. for filepath in glob_matches:
  80. image_saver.delete(
  81. filepath,
  82. storage_type=image_storage_type_orig,
  83. bucket_name=image_bucket_name_orig)
  84. else:
  85. model.image = image_orig
  86. # Handle image deletion
  87. if form.image_delete.data and image_orig:
  88. filepath = path.join(
  89. app.config['UPLOADS_FOLDER'], image_orig)
  90. # Delete the file. In this case, we have to pass in
  91. # arguments specifying whether to delete locally or on
  92. # S3, as this should depend on where the file was
  93. # originally saved, rather than on how S3Saver was
  94. # initialised.
  95. image_saver.delete(filepath,
  96. storage_type=image_storage_type_orig,
  97. bucket_name=image_bucket_name_orig)
  98. # Also delete thumbnails
  99. glob_filepath_split = path.splitext(path.join(
  100. app.config['MEDIA_THUMBNAIL_FOLDER'],
  101. image_orig))
  102. glob_filepath = glob_filepath_split[0]
  103. # S3Saver can search for files too. When searching locally,
  104. # it uses glob(); when searching on S3, it uses key
  105. # prefixes.
  106. glob_matches = image_saver.find_by_path(
  107. glob_filepath,
  108. storage_type=image_storage_type_orig,
  109. bucket_name=image_bucket_name_orig)
  110. for filepath in glob_matches:
  111. image_saver.delete(filepath,
  112. storage_type=image_storage_type_orig,
  113. bucket_name=image_bucket_name_orig)
  114. model.image = ''
  115. model.image_storage_type = ''
  116. model.image_storage_bucket_name = ''
  117. if form.image.data or form.image_delete.data:
  118. db.session.add(model)
  119. db.session.commit()
  120. flash('Thingy %s' % (
  121. form.image_delete.data and 'deleted' or 'saved'),
  122. 'success')
  123. else:
  124. flash(
  125. 'Please upload a new thingy or delete the ' +
  126. 'existing thingy',
  127. 'warning')
  128. return redirect(url_for('foo.home'))
  129. return render_template('home.html',
  130. form=form,
  131. model=model)