/**
   * Stores a photo on S3 and then updates the underlying Photo object with the URL's that point to
   * the image locations on S3.
   *
   * <p>The scaling process creates three separate images, a web size, a thumbnail, and then the
   * untouched original upload. The original is stored using full S3 redundancy, but the thumbnail
   * and web size versions are stored using reduced redundancy. In the event of a loss of reduced
   * redundancy data, the thumbnail and websize data could be regenerated if need be but that's not
   * implemented here.
   *
   * @param photo the photo data to be stored (should be initialized with a photo ID before being
   *     passed in)
   * @param photoData raw data for the photo itself
   * @throws IOException
   */
  public static Photo storePhoto(Photo photo, byte[] photoData) throws IOException {

    // Store various photo sizes
    String thumbnailPath = storeThumbnail(photo, photoData);
    photo.setThumbnailPath(thumbnailPath);

    String websizePath = storeWebsize(photo, photoData);
    photo.setWebsizePath(websizePath);

    String originalPath = storeOriginal(photo, photoData);
    photo.setOriginalPath(originalPath);

    return photo;
  }
 /**
  * Scales the incoming photo data to a web size, stores it on S3, and then returns the storage
  * path for the photo on S3. This method uses reduced redundancy storage to reduce storage costs.
  * Should there be a loss of the data on S3, it could be regenerated from the original full size
  * image.
  *
  * @param photo metadata for the photo
  * @param photoData the raw data from the photo
  * @return storage path used on S3 (derived from the id of the photo and a predetermined suffix)
  * @throws IOException
  */
 private static String storeWebsize(Photo photo, byte[] photoData) throws IOException {
   byte[] websize = scalePhoto(WEBSIZE_LONG_EDGE, photoData);
   TravelLogStorageObject obj = getStorageObject(websize, photo.getId() + WEBSIZE_SUFFIX);
   S3StorageManager mgr = new S3StorageManager();
   mgr.storePublicRead(obj, true);
   return obj.getAwsUrl();
 }
 /**
  * Scales the incoming photo data to a thumbnail size, stores it on S3, and then returns the
  * storage path for the photo on S3. This method uses reduced redundancy storage to reduce storage
  * costs. Should there be a loss of the thumbnail data on S3, it could be regenerated from the
  * original full size image.
  *
  * @param photo metadata for the photo
  * @param photoData the raw data from the photo
  * @return storage path used on S3 (derived from the id of the photo and a predetermined suffix)
  * @throws IOException
  */
 private static String storeThumbnail(Photo photo, byte[] photoData) throws IOException {
   byte[] thumbnail = scalePhoto(THUMBNAIL_LONG_EDGE, photoData);
   TravelLogStorageObject obj = getStorageObject(thumbnail, photo.getId() + THUMB_SUFFIX);
   S3StorageManager mgr = new S3StorageManager();
   mgr.storePublicRead(obj, true);
   return obj.getAwsUrl();
 }
 /**
  * Loads original photo from S3, returning the raw image data.
  *
  * @param photo the photo object specifying storage path of the original photo
  * @return raw image data
  * @throws IOException
  */
 public static InputStream loadOriginalPhoto(Photo photo) throws IOException {
   S3StorageManager mgr = new S3StorageManager();
   TravelLogStorageObject obj = new TravelLogStorageObject();
   obj.setBucketName(uniqueBucketName);
   obj.setStoragePath(photo.getId() + FULLSIZE_SUFFIX);
   return mgr.loadInputStream(obj);
 }
  /**
   * Deletes a photo from S3 based on the paths stored in the Photo object. This will delete both
   * the original photo and the resized versions that were created during the storage process.
   *
   * @param photo the photo to be deleted
   */
  public static void deletePhotoFromS3(Photo photo) {

    if (photo.getOriginalPath() != null) {
      deleteFromPath(photo.getId() + FULLSIZE_SUFFIX);
    }
    if (photo.getThumbnailPath() != null) {
      deleteFromPath(photo.getId() + THUMB_SUFFIX);
    }
    if (photo.getWebsizePath() != null) {
      deleteFromPath(photo.getId() + WEBSIZE_SUFFIX);
    }
  }
 /**
  * Stores the original full size image on S3, and then returns the storage path for the photo on
  * S3. This method uses full redundancy storage because if we lose the original there's no way to
  * recreate it. Also, in the event of data loss of a thumbnail or web size of the original, this
  * could be used to recreate the smaller images.
  *
  * @param photo metadata for the photo
  * @param photoData the raw data from the photo
  * @return storage path used on S3 (derived from the id of the photo and a predetermined suffix)
  * @throws IOException
  */
 private static String storeOriginal(Photo photo, byte[] photoData) throws IOException {
   TravelLogStorageObject obj = getStorageObject(photoData, photo.getId() + FULLSIZE_SUFFIX);
   S3StorageManager mgr = new S3StorageManager();
   mgr.storePublicRead(obj, false);
   return obj.getAwsUrl();
 }