shrinerb / shrine

File Attachment toolkit for Ruby applications

Home Page:https://shrinerb.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

filename not populating in image_data in rails 7 with filepond

kbachand opened this issue · comments

I have a rails 7 app with shrinerb and filepond. I am having issues with the filename sticking in metadata. I have tried a lot of workarounds with nothing working. It seems no matter what the value is null EXCEPT when I first upload initially its there. It seems its lost in space at some point between initial upload and cache storage where it doesn't stay in image_data.

uploads_controller.rb create action:

  def create
    begin
      uploaded_file = ImageUploader.upload(params[:file], :cache)
      Rails.logger.debug "Metadata: #{uploaded_file.metadata.inspect}"
      if uploaded_file
        render json: { id: uploaded_file.id, storage: 'cache', metadata: uploaded_file.metadata }
        Rails.logger.debug "UPLOADED_FILE_Metadata: #{uploaded_file.metadata.inspect}"
      else
        render json: { error: "File upload failed" }, status: :unprocessable_entity
      end
    rescue => e
      Rails.logger.error "Upload failed: #{e.message}"
      render json: { error: e.message }, status: :unprocessable_entity
    end
  end

OUTPUT from uploads create with debug logging:

Started POST "/uploads" for 127.0.0.1 at 2024-04-05 16:38:13 -0400
Processing by UploadsController#create as */*
  Parameters: {"file"=>#<ActionDispatch::Http::UploadedFile:0x00000218f792cfe0 @tempfile=#<Tempfile:C:/Users/keith/AppData/Local/Temp/RackMultipart20240405-118100-uq8i0z.jpg>, @content_type="image/jpeg", @original_filename="3S0A3507.jpg", @headers="Content-Disposition: form-data; name=\"file\"; filename=\"3S0A3507.jpg\"\r\nContent-Type: image/jpeg\r\n">, "fileData"=>"{\"filename\":\"3S0A3507.jpg\"}"}
Metadata: {"filename"=>"3S0A3507.jpg", "size"=>31576058, "mime_type"=>"image/jpeg", "width"=>8192, "height"=>5464}
UPLOADED_FILE_Metadata: {"filename"=>"3S0A3507.jpg", "size"=>31576058, "mime_type"=>"image/jpeg", "width"=>8192, "height"=>5464}
Completed 200 OK in 50ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 1897)

From here it goes to my locations_controller where I have a create action which will call an attach method to attach the images to the location.

my locations controller create action:

  def create
      @location = current_user.locations.build(location_params.except(:uploaded_photos))
      puts params.inspect
       # This is for debugging; consider removing it in production
      if params[:location][:uploaded_photos].present?
        attach_uploaded_photos(@location, params[:location][:uploaded_photos])
      end

      if @location.save
        respond_to do |format|
          format.html { redirect_to locations_path }
          format.turbo_stream do
            render turbo_stream: turbo_stream.append("locationsUl", partial: "location", locals: { location: @location })
          end
          # Handle JSON response for AJAX requests
          format.json { render json: @location, status: :created }
        end
      else
        respond_to do |format|
          format.html { render :new, status: :unprocessable_entity }
          format.turbo_stream { render turbo_stream: turbo_stream.replace(@location, partial: "locations/form", locals: { location: @location }) }
          # Handle JSON response for errors during AJAX requests
          format.json { render json: @location.errors.full_messages, status: :unprocessable_entity }
        end
      end
    end

locations controller attach:

    def attach_uploaded_photos(location, uploaded_photo_ids)
      uploaded_photo_ids.each do |photo_id|
        uploaded_file = ImageUploader.uploaded_file(storage: 'cache', id: photo_id)
        uploaded_file.refresh_metadata!
        if uploaded_file.exists?
          Rails.logger.debug "Cached file: #{uploaded_file.inspect}"

          # No need for file_data parsing, directly use image_data
          photo = location.photos.build(image: uploaded_file, image_data: uploaded_file.metadata)

          if photo.save
            Rails.logger.debug "Photo attached successfully with metadata: #{photo.image.metadata.inspect}"
          else
            Rails.logger.error "Failed to attach photo: #{photo.errors.full_messages.join(", ")}"
          end
        else
          Rails.logger.error "Uploaded file with ID #{photo_id} not found or does not exist."
        end
      end
    end

From start to end logging:

Started POST "/uploads" for 127.0.0.1 at 2024-04-05 16:44:26 -0400
Processing by UploadsController#create as */*
  Parameters: {"file"=>#<ActionDispatch::Http::UploadedFile:0x0000020cba6ce7d8 @tempfile=#<Tempfile:C:/Users/keith/AppData/Local/Temp/RackMultipart20240405-116412-dnmgv8.jpg>, @content_type="image/jpeg", @original_filename="3S0A3507.jpg", @headers="Content-Disposition: form-data; name=\"file\"; filename=\"3S0A3507.jpg\"\r\nContent-Type: image/jpeg\r\n">, "fileData"=>"{\"filename\":\"3S0A3507.jpg\"}"}
Metadata: {"filename"=>"3S0A3507.jpg", "size"=>31576058, "mime_type"=>"image/jpeg", "width"=>8192, "height"=>5464}
UPLOADED_FILE_Metadata: {"filename"=>"3S0A3507.jpg", "size"=>31576058, "mime_type"=>"image/jpeg", "width"=>8192, "height"=>5464}
Completed 200 OK in 50ms (Views: 0.2ms | ActiveRecord: 0.0ms | Allocations: 2350)


Started POST "/locations" for 127.0.0.1 at 2024-04-05 16:44:28 -0400
Processing by LocationsController#create as TURBO_STREAM
  Parameters: {"authenticity_token"=>"[FILTERED]", "location"=>{"name"=>"", "details"=>"", "street_address"=>"", "location_type"=>"Indoor", "uploaded_photos"=>["eda3b15fc85d884cd7280d8ffc1bf6fc.jpg"]}, "file"=>"{\"id\":\"eda3b15fc85d884cd7280d8ffc1bf6fc.jpg\",\"storage\":\"cache\",\"metadata\":{\"filename\":\"3S0A3507.jpg\",\"size\":31576058,\"mime_type\":\"image/jpeg\",\"width\":8192,\"height\":5464}}", "commit"=>"Add Location"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ?  [["id", 4], ["LIMIT", 1]]
#<ActionController::Parameters {"authenticity_token"=>"WqHPOPhdNuni0N5C2BW2IUiLCdra5mpwnH7xNd_PTaVd7_bWJqBBESwNdsyR1vKoAk5Z1OBw7SRcFDNtsBHUkA", "location"=>#<ActionController::Parameters {"name"=>"", "details"=>"", "street_address"=>"", "location_type"=>"Indoor", "uploaded_photos"=>["eda3b15fc85d884cd7280d8ffc1bf6fc.jpg"]} permitted: false>, "file"=>"{\"id\":\"eda3b15fc85d884cd7280d8ffc1bf6fc.jpg\",\"storage\":\"cache\",\"metadata\":{\"filename\":\"3S0A3507.jpg\",\"size\":31576058,\"mime_type\":\"image/jpeg\",\"width\":8192,\"height\":5464}}", "commit"=>"Add Location", "controller"=>"locations", "action"=>"create"} permitted: false>
Cached file: #<ImageUploader::UploadedFile storage=:cache id="eda3b15fc85d884cd7280d8ffc1bf6fc.jpg" metadata={"filename"=>nil, "size"=>31576058, "mime_type"=>"image/jpeg", "width"=>8192, "height"=>5464}>
  TRANSACTION (0.1ms)  begin transaction
  ↳ app/controllers/locations_controller.rb:131:in `block in attach_uploaded_photos'
  Location Create (15.1ms)  INSERT INTO "locations" ("name", "details", "street_address", "city", "state", "zip_code", "created_at", "updated_at", "user_id", "location_type") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)  [["name", ""], ["details", ""], ["street_address", ""], ["city", nil], ["state", nil], ["zip_code", nil], ["created_at", "2024-04-05 20:44:28.256691"], ["updated_at", "2024-04-05 20:44:28.256691"], ["user_id", 4], ["location_type", "Indoor"]]
  ↳ app/controllers/locations_controller.rb:131:in `block in attach_uploaded_photos'
  Photo Create (0.2ms)  INSERT INTO "photos" ("location_id", "image_data", "created_at", "updated_at", "file_id") VALUES (?, ?, ?, ?, ?)  [["location_id", 6], ["image_data", "{\"filename\"=>nil, \"size\"=>31576058, \"mime_type\"=>\"image/jpeg\", \"width\"=>8192, \"height\"=>5464}"], ["created_at", "2024-04-05 20:44:28.276100"], ["updated_at", "2024-04-05 20:44:28.276100"], ["file_id", nil]]
  ↳ app/controllers/locations_controller.rb:131:in `block in attach_uploaded_photos'
  TRANSACTION (4.8ms)  commit transaction
  ↳ app/controllers/locations_controller.rb:131:in `block in attach_uploaded_photos'
  TRANSACTION (0.0ms)  begin transaction
  ↳ app/controllers/locations_controller.rb:131:in `block in attach_uploaded_photos'
  Photo Update (0.9ms)  UPDATE "photos" SET "image_data" = ?, "updated_at" = ? WHERE "photos"."id" = ?  [["image_data", "{\"id\":\"05131c3fafcdda37691051426f99187f.jpg\",\"storage\":\"store\",\"metadata\":{\"filename\":null,\"size\":31576058,\"mime_type\":\"image/jpeg\",\"width\":8192,\"height\":5464}}"], ["updated_at", "2024-04-05 20:44:28.344441"], ["id", 20]]
  ↳ app/controllers/locations_controller.rb:131:in `block in attach_uploaded_photos'
  TRANSACTION (4.3ms)  commit transaction
  ↳ app/controllers/locations_controller.rb:131:in `block in attach_uploaded_photos'
Photo attached successfully with metadata: {"filename"=>nil, "size"=>31576058, "mime_type"=>"image/jpeg", "width"=>8192, "height"=>5464}
  Rendered locations/_location.html.erb (Duration: 0.9ms | Allocations: 537)
Completed 200 OK in 191ms (Views: 0.1ms | ActiveRecord: 25.6ms | Allocations: 25633)

photo model:

class Photo < ApplicationRecord
  include ImageUploader::Attachment(:image) # Assumes you have an ImageUploader for Shrine
  belongs_to :location, optional: true
end

shrinerb initializer:

require "shrine"
require "shrine/storage/file_system"

if Rails.env.production?
  require "shrine/storage/google_cloud_storage"
  # Configure Shrine to use Google Cloud Storage in production
else
  # Use local file system storage in development and test environments
  Shrine.storages = {
    cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary
    store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"), # permanent
  }
end
# Common Shrine plugins and configurations
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
Shrine.plugin :restore_cached_data
Shrine.plugin :refresh_metadata
Shrine.plugin :store_dimensions
Shrine.plugin :determine_mime_type, analyzer: :marcel
Shrine.plugin :metadata_attributes # for persisting metadata
Shrine.plugin :derivatives
Shrine.plugin :metadata_attributes, filename: :filename

image_uploader:

require "shrine"

class ImageUploader < Shrine
  # Plugin configurations and custom logic here
end

What I expect:
-Shrinerb to be able to handle the metadata, specifically the filename from upload to storage cache, to storage store(permanent).

What I am seeing:
-Filepond is providing the metadata and once its handed off the shrinerb the filename is not sticking.

I am assuming this is an issue with shrinerb itself. I am using gem 'shrine', '~> 3.5'

When you receive a POST /location, I see the uploaded file data being sent in file param (which includes the filename), but I don't see the controller actually using that data to form an uploaded file object. It only seems to use uploaded file IDs and refreshes the metadata, which won't retrieve the original filename, as it's not stored on the storage service.

So, the reason the original filename is getting lost is because you're not passing it all the way through.