Limit total size (MBs) of uploads per user to AWS S3 (using Shrine)

703 views Asked by At

I am working on a Rails application that allows users to upload their images to AWS S3 via shrine. The setup so far works just fine - however, I was wondering if there is any straight forward of extracting the total size of uploads per user.

For example, I do want free users only to be allowed uploading max 500MB, while members get say 4GB.

Haven't found anything useable on Github or Google, in general, perhaps someone can share their knowledge. Cheers !

Current code:

photo.rb

class Photo < ApplicationRecord
  include ImageUploader[:image]

  before_create :set_name

  belongs_to :employee
  belongs_to :user

  private

    def set_name
      self.name = "Photo"
    end
end

image_uploader.rb

require "image_processing/mini_magick"

class ImageUploader < Shrine
  include ImageProcessing::MiniMagick
  plugin :determine_mime_type
  plugin :remove_attachment
  plugin :store_dimensions
  plugin :validation_helpers
  plugin :pretty_location
  plugin :processing
  plugin :versions

  Attacher.validate do
    validate_max_size 5.megabytes, message: "is too large (max is 5 MB)"
    validate_mime_type_inclusion %w[image/jpeg image/png], message: " extension is invalid. Please upload JPEGs, JPGs and PNGs only."
    validate_min_width 400, message: " must have a minimum width of 400 pixel."
    validate_min_height 250, message: " must have a minimum height of 250 pixel."
  end

  process(:store) do |io, context|

    size_800 = resize_to_limit!(io.download, 800, 800)
    size_300 = resize_to_limit!(io.download, 300, 300)

    {original: io, large: size_800, small: size_300}
  end
end
2

There are 2 answers

1
Janko On BEST ANSWER

If you want to add the validation error message on the image column of the Photo model, you do this in Shrine validations since you can access the Photo record there.

class ImageUploader < Shrine
  # ...

  Attacher.validate do
    record #=> #<Photo ...>

    all_images = record.user.photos
      .select { |photo| photo.image_attacher.stored? }
      .map { |photo| photo.image[:large] }

    total_size = all_images.map(&:size).inject(0, :+)

    if total_size + get.size > 500.megabytes
      errors << "exceeds total maximum filesize for your account (max is 500MB)"
    end

    # ...
  end
end

First we're filtering only attachments which are present and uploaded to permanent storage (e.g. if you use backgrounding there will be a period when only the cached file is attached).

Then we're summing up all the image sizes of large versions, as in your example.

Finally, we're adding the current attachment size to the total size, and if it exceeds the limit we're adding a validation error.

0
Georg Keferböck On

So this is my solution - a custom validator. Very basic, but I think this shall do (not fully tested, need to catch a flight):

class Photo < ApplicationRecord
  include ImageUploader[:image]
  include ActiveModel::Validations

  before_create :set_name

  belongs_to :employee
  belongs_to :user

  validate :upload_size_validation

  private

    def set_name
      self.name = "Photo"
    end

    def upload_size_validation
      size = 0

      user.photos.each do |photo|
        byebug
        size = size + photo.image[:large].size.to_i
      end

      errors[:photo] << " cannot be uploaded as you hit the quota of 5 MB" if size > 50000
    end
end

If there is a better solution, please share !