Image upload on cloudinary impossible from tmp/uploads

Good morning,

I have an Ingredient controller which allows you to massively associate photos with a series of ingredients.

In a local development environment, everything works well. I use a tmp/uploads folder to store photos locally to finally import the selected photos at the end to Cloudinary via the associate_photos method in the service (I have also already tried via the public/uploads folder).

On Heroku, it’s impossible to move forward, I’ve been going around in circles for many hours, it seems like I can’t find the photos when I upload them in the associate_photos method. However, they are clearly visible on my “review_photos” view, even after 15 minutes, so there is no problem with immediate deletion of the contents of the tmp folder and which proves that these photos are indeed somewhere….

I know that Rails is not optimized for local storage, and I realize that everything will be deleted once a day or on a new release, but I only need to keep these images for 10 – 15 minutes maximum.

Why not upgrade to Cloudinary or AWS type storage? Because loading takes longer. If I load 50 images, but I decide to only keep 5, I will have to wait a long time to load the 50 images and finally only keep 5…

It seems that the files disappear between the review method and the associate method, but then how can I explain that if I go back in the browser, the images appear again and remain visible?

Code of Ingredient controller :

class IngredientsController < ApplicationController
  include CloudinaryHelper
  before_action :set_supplier, only: [:upload_photos, :review_photos]
  skip_before_action :verify_authenticity_token, only: [:serve_temp_upload]

  def upload_photos_form
    authorize :ingredient
    @suppliers = policy_scope(Supplier).order(:name)
  end

  def upload_photos
    skip_authorization
    @supplier = Supplier.find(params[:supplier_id])

    photo_service = Ingredients::PhotoService.new(params, current_user)
    @uploaded_photos = photo_service.upload_photos(@supplier)

    # Log pour vérifier l'existence des fichiers
    @uploaded_photos.each do |photo|
      filepath = Rails.root.join('tmp', 'uploads', current_user.master_account_id.to_s, photo.filename)
      Rails.logger.info "File exists: #{File.exist?(filepath)} - Path: #{filepath}"
    end
    sleep(1) # Ajoute un délai d'une seconde
    redirect_to review_photos_ingredients_path(supplier_id: @supplier.id, uploaded_photos: @uploaded_photos.map(&:id))
  rescue ActiveRecord::RecordInvalid => e
    Rails.logger.error "Validation failed: #{e.record.errors.full_messages.join(', ')}"
    sleep(1) # Ajoute un délai d'une seconde en cas d'erreur
    redirect_to ingredients_upload_photos_path(supplier_id: @supplier.id), alert: "Failed to upload photos. Please try again."
  end

  def review_photos
    authorize :ingredient
    @supplier = Supplier.find(params[:supplier_id])
    photo_service = Ingredients::PhotoService.new(params, current_user)
    result = photo_service.review_photos(params[:uploaded_photos], @supplier)

    @supplier = result[:supplier]
    @uploaded_photos = result[:uploaded_photos]

    # Ajout de logs pour vérifier l'existence des fichiers
    @uploaded_photos.each do |photo|
      filepath = Rails.root.join('tmp', 'uploads', @current_user.master_account_id.to_s, photo.filename)
      Rails.logger.info "Review File exists: #{File.exist?(filepath)} - Path: #{filepath}"
    end

    render :review_photos
  end

  def associate_photos
    authorize :ingredient

    # Passer les données nécessaires au job Sidekiq
    AssociatePhotosJob.perform_later(current_user.id, photos_data_params)

    redirect_to ingredients_path
  end

  def check_photo
    authorize :ingredient
    photo_service = Ingredients::PhotoService.new(params, current_user)
    result = photo_service.check_photo(params[:id])

    render json: result
  end

  def serve_temp_upload
    skip_authorization
    file_path = Rails.root.join('tmp', 'uploads', current_user.master_account_id.to_s, "#{params[:filename]}.#{params[:format]}")

    # Ajoutez un log pour vérifier le chemin d'accès
    Rails.logger.debug "Trying to access file at: #{file_path}"

    if File.exist?(file_path)
      send_file file_path, type: Mime::Type.lookup_by_extension(params[:format]), disposition: 'inline'
    else
      Rails.logger.error "File not found at: #{file_path}"
      render plain: 'File not found', status: :not_found
    end
  end


  private

  def permit_photos_data
    params.require(:photos_data).map do |photo|
      photo.permit(:filename, :ingredient_id)
    end
  end

  def photos_data_params
    params.require(:photos_data).map do |photo|
      photo.permit(:filename, :ingredient_id)
    end
  end


  def set_supplier
    @supplier = Supplier.find(params[:supplier_id]) if params[:supplier_id]
  end


end

Photo Service :

module Ingredients
  class PhotoService
    include CloudinaryHelper

    def initialize(params, current_user)
      @params = params
      @current_user = current_user
      @uploaded_photos = []
      @master_account_id = current_user.master_account_id
    end

    def upload_photos(supplier)
      Rails.logger.info "##############  START UPLOAD PHOTOS ##############"
      uploaded_photos = []
      uploads_dir = Rails.root.join('tmp', 'uploads', @master_account_id.to_s)

      FileUtils.mkdir_p(uploads_dir)

      clean_uploads_directory(uploads_dir)

      @params[:photos].each do |photo|
        next if photo.blank?

        filename = SecureRandom.hex + File.extname(photo.original_filename)
        filepath = uploads_dir.join(filename)

        FileUtils.mkdir_p(File.dirname(filepath))
        File.open(filepath, 'wb') do |file|
          file.write(photo.read)
        end
        Rails.logger.info "##################### File saved at: #{filepath}"

        photo_upload = PhotoUpload.new(
          user: @current_user,
          supplier: supplier,
          filename: filename,
          original_filename: photo.original_filename,
          filepath: filepath.to_s
        )

        if photo_upload.save
          uploaded_photos << photo_upload
        else
          Rails.logger.error "Failed to save photo_upload: #{photo_upload.errors.full_messages.join(', ')}"
        end
      end
      Rails.logger.info "##############  END UPLOAD PHOTOS ##############"
      uploaded_photos
    end

    def review_photos(photo_ids, supplier)
      Rails.logger.info "##############  START REVIEW PHOTOS ##############"
      @uploaded_photos = PhotoUpload.where(id: photo_ids, user: @current_user, supplier_id: supplier.id)
      Rails.logger.info "##############  Uploaded photos: #{@uploaded_photos}"
      uploaded_files = Dir.glob(Rails.root.join('tmp', 'uploads', @master_account_id.to_s, '*'))
      Rails.logger.info "##############  Uploaded files: #{uploaded_files}"

      @uploaded_photos.each do |photo|
        filename_without_extension = File.basename(photo.original_filename, File.extname(photo.original_filename))
        Rails.logger.info "##############  Filename without extension: #{filename_without_extension}"
        normalized_filename = I18n.transliterate(filename_without_extension.strip.downcase)
        Rails.logger.info "##############  Normalized filename: #{normalized_filename}"

        matched_ingredient = supplier.ingredients.find do |ingredient|
          normalized_ean_code = I18n.transliterate(ingredient.ean_code.strip.downcase) if ingredient.ean_code
          normalized_supplier_code = I18n.transliterate(ingredient.supplier_code.strip.downcase) if ingredient.supplier_code
          normalized_name_supplier_ref = I18n.transliterate(ingredient.name_supplier_ref.strip.downcase) if ingredient.name_supplier_ref

          normalized_filename == normalized_ean_code ||
          normalized_filename == normalized_supplier_code ||
          normalized_filename == normalized_name_supplier_ref
        end

        photo.update(ingredient_id: matched_ingredient ? matched_ingredient.id : nil)
      end
      Rails.logger.info "##############  END REVIEW PHOTOS ##############"

      {
        supplier: supplier,
        uploaded_photos: @uploaded_photos
      }
    end


    def associate_photos(photos_data)
      Rails.logger.info "#####  START ASSOCIATE PHOTOS ##############"
      Rails.logger.info "#####  Photos data: #{photos_data}"
      associated_files = []

      upload_path = Rails.root.join('tmp', 'uploads', @master_account_id.to_s)
      Rails.logger.info "##  Checking directory existence: #{upload_path}"
      unless Dir.exist?(upload_path)
        Rails.logger.error "######  Directory does not exist: #{upload_path}"
        return []
      end

      uploaded_files = Dir.glob(upload_path.join('*'))
      Rails.logger.info "######  Uploaded files: #{uploaded_files}"

      photos_data.each do |photo_data|
        filename = photo_data[:filename]
        ingredient_id = photo_data[:ingredient_id]
        photo = PhotoUpload.find_by(filename: filename, user: @current_user)
        photo_path = Rails.root.join('tmp', 'uploads', @master_account_id.to_s, filename)
        Rails.logger.info "######  Photo path: #{photo_path}"
        Rails.logger.info "######  Files in directory: #{Dir.entries(upload_path)}"


        if ingredient_id.present?
          Rails.logger.info "########  Ingredient ID: #{ingredient_id}"
          ingredient = Ingredient.find(ingredient_id)

          if File.exist?(photo_path)
            Rails.logger.info "######  File exists: #{photo_path}"
            ingredient.photo.purge if ingredient.photo.attached?

            # ingredient.photo.attach(io: File.open(photo_path), filename: filename, content_type: 'image/jpg')

            content_type = Mime::Type.lookup_by_extension(File.extname(photo_path).delete('.')).to_s
            Rails.logger.info "Content type: #{content_type}"
            uploaded_file = File.open(photo_path)
            Rails.logger.info "Uploaded file: #{uploaded_file}"

            # Upload to Cloudinary
            cloudinary_response = Cloudinary::Uploader.upload(uploaded_file, folder: "ingredients/#{ingredient.id}")
            Rails.logger.info "Cloudinary response: #{cloudinary_response}"
            ingredient.photo.attach(io: StringIO.new(cloudinary_response['url']), filename: filename, content_type: content_type)

            associated_files << filename
            Rails.logger.info "Successfully associated file: #{filename} with ingredient: #{ingredient_id}"
          else
            Rails.logger.error "File does not exist: #{photo_path}"
          end
        else
          Rails.logger.error "Ingredient ID is missing for file: #{filename}"
        end

        File.delete(photo_path) if File.exist?(photo_path)
        photo.destroy if photo
      end
      Rails.logger.info "##############  END ASSOCIATE PHOTOS ##############"
      associated_files
    end

    def check_photo(ingredient_id)
      ingredient = Ingredient.find(ingredient_id)
      has_photo = ingredient.photo.attached?
      photo_url = has_photo ? cl_image_path(ingredient.photo.key, height: 190, crop: 'fit') : nil

      {
        has_photo: has_photo,
        warning_message: I18n.t("Existing image will be overwritten"),
        photo_url: photo_url,
        ingredient_name: ingredient.name_short
      }
    end

    def self.clean_uploads_directory(user)
      tmp_dir = Rails.root.join('tmp', 'uploads', user.master_account_id.to_s)
      FileUtils.rm_rf(Dir.glob("#{tmp_dir}/*"))
    end

    private

    def clean_uploads_directory(uploads_dir)
      return unless File.directory?(uploads_dir)

      Dir.foreach(uploads_dir) do |filename|
        file_path = File.join(uploads_dir, filename)
        File.delete(file_path) if File.file?(file_path)
      end
    end
  end
end

Code of Job :

class AssociatePhotosJob < ApplicationJob
  queue_as :default

  def perform(user_id, photos_data)
    user = User.find(user_id)
    NotificationChannel.broadcast_to(user, message: I18n.t("The photo import has just started, and you'll receive a warning when it's finished."))
    photo_service = Ingredients::PhotoService.new({}, user)
    photo_service.associate_photos(photos_data)
    NotificationChannel.broadcast_to(user, message: I18n.t('Photos successfully associated with ingredients'))
  end
end

Logs in console heroku for uploads / review in service :

2024-06-27T10:33:47.250596+00:00 app[web.1]: I, [2024-06-27T10:33:47.250506 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] Started POST "/en/ingredients/upload_photos" for 85.26.49.18 at 2024-06-27 10:33:47 +0000
2024-06-27T10:33:47.251825+00:00 app[web.1]: I, [2024-06-27T10:33:47.251760 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] Processing by IngredientsController#upload_photos as HTML
2024-06-27T10:33:47.251894+00:00 app[web.1]: I, [2024-06-27T10:33:47.251861 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a]   Parameters: {"authenticity_token"=>"[FILTERED]", "photos"=>["", #<ActionDispatch::Http::UploadedFile:0x00007fafc5de8bc8 @tempfile=#<Tempfile:/tmp/RackMultipart20240627-2-en0og5.jpg>, @content_type="image/jpeg", @original_filename="151310.jpg", @headers="Content-Disposition: form-data; name="photos[]"; filename="151310.jpg"rnContent-Type: image/jpegrn">, #<ActionDispatch::Http::UploadedFile:0x00007fafc5de8a88 @tempfile=#<Tempfile:/tmp/RackMultipart20240627-2-uy1qjz.jpg>, @content_type="image/jpeg", @original_filename="151311.jpg", @headers="Content-Disposition: form-data; name="photos[]"; filename="151311.jpg"rnContent-Type: image/jpegrn">], "supplier_id"=>"1", "commit"=>"Upload", "locale"=>"en"}
2024-06-27T10:33:47.256844+00:00 app[web.1]: I, [2024-06-27T10:33:47.256785 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] ##############  START UPLOAD PHOTOS ##############
2024-06-27T10:33:47.257907+00:00 app[web.1]: I, [2024-06-27T10:33:47.257849 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] ##################### File saved at: /app/tmp/uploads/2/fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg
2024-06-27T10:33:47.263100+00:00 app[web.1]: I, [2024-06-27T10:33:47.263048 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] ##################### File saved at: /app/tmp/uploads/2/829217fbf3d58d69de8acc35ec3f77ba.jpg
2024-06-27T10:33:47.267099+00:00 app[web.1]: I, [2024-06-27T10:33:47.267043 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] ##############  END UPLOAD PHOTOS ##############
2024-06-27T10:33:47.267226+00:00 app[web.1]: I, [2024-06-27T10:33:47.267187 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] File exists: true - Path: /app/tmp/uploads/2/fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg
2024-06-27T10:33:47.267304+00:00 app[web.1]: I, [2024-06-27T10:33:47.267282 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] File exists: true - Path: /app/tmp/uploads/2/829217fbf3d58d69de8acc35ec3f77ba.jpg
2024-06-27T10:33:48.268115+00:00 app[web.1]: I, [2024-06-27T10:33:48.267991 #2]  INFO -- : [07825ef4-2519-4ff2-899c-0a58cf1f051a] Redirected to https://gestion-restaurant-2c24499aa5da.herokuapp.com/en/ingredients/review_photos?supplier_id=1&uploaded_photos%5B%5D=107&uploaded_photos%5B%5D=108
2024-06-27T10:33:48.369422+00:00 app[web.1]: I, [2024-06-27T10:33:48.369338 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] Started GET "/en/ingredients/review_photos?supplier_id=1&uploaded_photos%5B%5D=107&uploaded_photos%5B%5D=108" for 85.26.49.18 at 2024-06-27 10:33:48 +0000
2024-06-27T10:33:48.370621+00:00 app[web.1]: I, [2024-06-27T10:33:48.370549 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] Processing by IngredientsController#review_photos as HTML
2024-06-27T10:33:48.370638+00:00 app[web.1]: I, [2024-06-27T10:33:48.370616 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df]   Parameters: {"supplier_id"=>"1", "uploaded_photos"=>["107", "108"], "locale"=>"en"}
2024-06-27T10:33:48.376140+00:00 app[web.1]: I, [2024-06-27T10:33:48.376089 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  START REVIEW PHOTOS ##############
2024-06-27T10:33:48.376618+00:00 app[web.1]: I, [2024-06-27T10:33:48.376580 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  Uploaded photos: #<PhotoUpload::ActiveRecord_Relation:0x00007fafc5d73d28>
2024-06-27T10:33:48.376777+00:00 app[web.1]: I, [2024-06-27T10:33:48.376752 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  Uploaded files: ["/app/tmp/uploads/2/829217fbf3d58d69de8acc35ec3f77ba.jpg", "/app/tmp/uploads/2/fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg"]
2024-06-27T10:33:48.378214+00:00 app[web.1]: I, [2024-06-27T10:33:48.378170 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  Filename without extension: 151310
2024-06-27T10:33:48.378253+00:00 app[web.1]: I, [2024-06-27T10:33:48.378232 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  Normalized filename: 151310
2024-06-27T10:33:48.390831+00:00 app[web.1]: I, [2024-06-27T10:33:48.390781 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  Filename without extension: 151311
2024-06-27T10:33:48.390890+00:00 app[web.1]: I, [2024-06-27T10:33:48.390866 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  Normalized filename: 151311
2024-06-27T10:33:48.397925+00:00 app[web.1]: I, [2024-06-27T10:33:48.397871 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] ##############  END REVIEW PHOTOS ##############
2024-06-27T10:33:48.398038+00:00 app[web.1]: I, [2024-06-27T10:33:48.398013 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] Review File exists: true - Path: /app/tmp/uploads/2/fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg
2024-06-27T10:33:48.398088+00:00 app[web.1]: I, [2024-06-27T10:33:48.398071 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] Review File exists: true - Path: /app/tmp/uploads/2/829217fbf3d58d69de8acc35ec3f77ba.jpg
2024-06-27T10:33:48.402857+00:00 app[web.1]: I, [2024-06-27T10:33:48.402803 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df]   Rendered ingredients/review_photos.erb within layouts/application (Duration: 4.4ms | Allocations: 7521)
2024-06-27T10:33:48.445188+00:00 app[web.1]: I, [2024-06-27T10:33:48.445121 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df]   Rendered layout layouts/application.html.erb (Duration: 46.7ms | Allocations: 21134)
2024-06-27T10:33:48.445745+00:00 app[web.1]: I, [2024-06-27T10:33:48.445663 #2]  INFO -- : [11d8db07-a3b2-4d42-9c67-874c590389df] Completed 200 OK in 75ms (Views: 25.3ms | ActiveRecord: 34.9ms | Allocations: 32066)
2024-06-27T10:33:48.682193+00:00 app[web.1]: I, [2024-06-27T10:33:48.682106 #2]  INFO -- : [ab66b32c-86c1-4ee2-afb3-5bdaae1837a5] Started GET "/en/serve_temp_upload/fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg" for 85.26.49.18 at 2024-06-27 10:33:48 +0000
2024-06-27T10:33:48.683232+00:00 app[web.1]: I, [2024-06-27T10:33:48.683096 #2]  INFO -- : [ab66b32c-86c1-4ee2-afb3-5bdaae1837a5] Processing by IngredientsController#serve_temp_upload as JPEG
2024-06-27T10:33:48.683233+00:00 app[web.1]: I, [2024-06-27T10:33:48.683157 #2]  INFO -- : [ab66b32c-86c1-4ee2-afb3-5bdaae1837a5]   Parameters: {"locale"=>"en", "filename"=>"fd110c8b8c205bfa1b3b064ad0ee9ed9"}
2024-06-27T10:33:48.686372+00:00 app[web.1]: I, [2024-06-27T10:33:48.686318 #2]  INFO -- : [ab66b32c-86c1-4ee2-afb3-5bdaae1837a5] Sent file /app/tmp/uploads/2/fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg (0.1ms)
2024-06-27T10:33:48.686481+00:00 app[web.1]: I, [2024-06-27T10:33:48.686458 #2]  INFO -- : [ab66b32c-86c1-4ee2-afb3-5bdaae1837a5] Completed 200 OK in 3ms (ActiveRecord: 0.8ms | Allocations: 820)
2024-06-27T10:33:48.785761+00:00 app[web.1]: I, [2024-06-27T10:33:48.785668 #2]  INFO -- : [9e9ac644-4789-4652-981e-ca412ad173c0] Started GET "/en/serve_temp_upload/829217fbf3d58d69de8acc35ec3f77ba.jpg" for 85.26.49.18 at 2024-06-27 10:33:48 +0000
2024-06-27T10:33:48.786956+00:00 app[web.1]: I, [2024-06-27T10:33:48.786890 #2]  INFO -- : [9e9ac644-4789-4652-981e-ca412ad173c0] Processing by IngredientsController#serve_temp_upload as JPEG
2024-06-27T10:33:48.786984+00:00 app[web.1]: I, [2024-06-27T10:33:48.786956 #2]  INFO -- : [9e9ac644-4789-4652-981e-ca412ad173c0]   Parameters: {"locale"=>"en", "filename"=>"829217fbf3d58d69de8acc35ec3f77ba"}
2024-06-27T10:33:48.790722+00:00 app[web.1]: I, [2024-06-27T10:33:48.790663 #2]  INFO -- : [9e9ac644-4789-4652-981e-ca412ad173c0] Sent file /app/tmp/uploads/2/829217fbf3d58d69de8acc35ec3f77ba.jpg (0.2ms)
2024-06-27T10:33:48.790858+00:00 app[web.1]: I, [2024-06-27T10:33:48.790828 #2]  INFO -- : [9e9ac644-4789-4652-981e-ca412ad173c0] Completed 200 OK in 4ms (ActiveRecord: 0.7ms | Allocations: 804)
2024-06-27T10:33:49.185975+00:00 app[web.1]: I, [2024-06-27T10:33:49.185924 #2]  INFO -- : [9b9ee34d-93fb-4b56-ad67-2ae128055836] Started GET "/ingredients/97/check_photo" for 85.26.49.18 at 2024-06-27 10:33:49 +0000
2024-06-27T10:33:49.186966+00:00 app[web.1]: I, [2024-06-27T10:33:49.186911 #2]  INFO -- : [9b9ee34d-93fb-4b56-ad67-2ae128055836] Processing by IngredientsController#check_photo as */*
2024-06-27T10:33:49.186985+00:00 app[web.1]: I, [2024-06-27T10:33:49.186966 #2]  INFO -- : [9b9ee34d-93fb-4b56-ad67-2ae128055836]   Parameters: {"id"=>"97"}
2024-06-27T10:33:49.188472+00:00 app[web.1]: I, [2024-06-27T10:33:49.188422 #2]  INFO -- : Registered connection (Z2lkOi8vZ2VzdGlvbi9Vc2VyLzM)
2024-06-27T10:33:49.195774+00:00 app[web.1]: I, [2024-06-27T10:33:49.195719 #2]  INFO -- : [97a632d2-2547-452b-ade4-af37a2aa3a10] Started GET "/ingredients/94/check_photo" for 85.26.49.18 at 2024-06-27 10:33:49 +0000
2024-06-27T10:33:49.196575+00:00 app[web.1]: I, [2024-06-27T10:33:49.196506 #2]  INFO -- : [97a632d2-2547-452b-ade4-af37a2aa3a10] Processing by IngredientsController#check_photo as */*
2024-06-27T10:33:49.196608+00:00 app[web.1]: I, [2024-06-27T10:33:49.196573 #2]  INFO -- : [97a632d2-2547-452b-ade4-af37a2aa3a10]   Parameters: {"id"=>"94"}
2024-06-27T10:33:49.205060+00:00 app[web.1]: I, [2024-06-27T10:33:49.205014 #2]  INFO -- : [97a632d2-2547-452b-ade4-af37a2aa3a10] Completed 200 OK in 8ms (Views: 0.2ms | ActiveRecord: 3.2ms | Allocations: 1768)

After this step, I see well the images in navigator in page review_photos. If I wait 15 minutes and refresh, I still see photos.

Logs in console when i click on associate :



2024-06-27T10:35:46.247239+00:00 heroku[router]: at=info method=POST path="/en/ingredients/associate_photos" host=gestion-restaurant-2c24499aa5da.herokuapp.com request_id=18b1d3db-be1e-462e-951f-fb037f1db53c fwd="85.26.49.18" dyno=web.1 connect=0ms service=21ms status=302 bytes=1241 protocol=https
2024-06-27T10:35:46.227028+00:00 app[web.1]: I, [2024-06-27T10:35:46.226936 #2]  INFO -- : [18b1d3db-be1e-462e-951f-fb037f1db53c] Started POST "/en/ingredients/associate_photos" for 85.26.49.18 at 2024-06-27 10:35:46 +0000
2024-06-27T10:35:46.228061+00:00 app[web.1]: I, [2024-06-27T10:35:46.227997 #2]  INFO -- : [18b1d3db-be1e-462e-951f-fb037f1db53c] Processing by IngredientsController#associate_photos as TURBO_STREAM
2024-06-27T10:35:46.228106+00:00 app[web.1]: I, [2024-06-27T10:35:46.228071 #2]  INFO -- : [18b1d3db-be1e-462e-951f-fb037f1db53c]   Parameters: {"authenticity_token"=>"[FILTERED]", "photos_data"=>[{"filename"=>"fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg", "ingredient_id"=>"97"}, {"filename"=>"829217fbf3d58d69de8acc35ec3f77ba.jpg", "ingredient_id"=>"94"}], "commit"=>"Associate photos", "locale"=>"en"}
2024-06-27T10:35:46.242823+00:00 app[web.1]: I, [2024-06-27T10:35:46.242742 #2]  INFO -- : [18b1d3db-be1e-462e-951f-fb037f1db53c] [ActiveJob] Enqueued AssociatePhotosJob (Job ID: f0778fa1-e2b8-41ca-91cf-29e4c6503893) to Sidekiq(default) with arguments: 3, [#<ActionController::Parameters {"filename"=>"fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg", "ingredient_id"=>"97"} permitted: true>, #<ActionController::Parameters {"filename"=>"829217fbf3d58d69de8acc35ec3f77ba.jpg", "ingredient_id"=>"94"} permitted: true>]
2024-06-27T10:35:46.243223+00:00 app[web.1]: I, [2024-06-27T10:35:46.243159 #2]  INFO -- : [18b1d3db-be1e-462e-951f-fb037f1db53c] Redirected to https://gestion-restaurant-2c24499aa5da.herokuapp.com/en/ingredients
2024-06-27T10:35:46.426921+00:00 app[web.1]: I, [2024-06-27T10:35:46.426812 #2]  INFO -- : [a57b9326-db06-4f93-b7f6-a22772909aba] Started GET "/en/ingredients" for 85.26.49.18 at 2024-06-27 10:35:46 +0000
2024-06-27T10:35:46.428076+00:00 app[web.1]: I, [2024-06-27T10:35:46.428005 #2]  INFO -- : [a57b9326-db06-4f93-b7f6-a22772909aba] Processing by IngredientsController#index as TURBO_STREAM
2024-06-27T10:35:46.428086+00:00 app[web.1]: I, [2024-06-27T10:35:46.428069 #2]  INFO -- : [a57b9326-db06-4f93-b7f6-a22772909aba]   Parameters: {"locale"=>"en"}
2024-06-27T10:35:46.242961+00:00 app[worker.1]: pid=2 tid=1okm class=AssociatePhotosJob jid=ac84b420282a6df91872f1e2 INFO: start
2024-06-27T10:35:46.243930+00:00 app[worker.1]: I, [2024-06-27T10:35:46.243815 #2]  INFO -- : [ActiveJob] [AssociatePhotosJob] [f0778fa1-e2b8-41ca-91cf-29e4c6503893] Performing AssociatePhotosJob (Job ID: f0778fa1-e2b8-41ca-91cf-29e4c6503893) from Sidekiq(default) enqueued at 2024-06-27T10:35:46Z with arguments: 3, [{"filename"=>"fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg", "ingredient_id"=>"97"}, {"filename"=>"829217fbf3d58d69de8acc35ec3f77ba.jpg", "ingredient_id"=>"94"}]
2024-06-27T10:35:46.279922+00:00 app[worker.1]: I, [2024-06-27T10:35:46.279808 #2]  INFO -- : [ActiveJob] [AssociatePhotosJob] [f0778fa1-e2b8-41ca-91cf-29e4c6503893] ##############  START ASSOCIATE PHOTOS ##############
2024-06-27T10:35:46.279956+00:00 app[worker.1]: I, [2024-06-27T10:35:46.279926 #2]  INFO -- : [ActiveJob] [AssociatePhotosJob] [f0778fa1-e2b8-41ca-91cf-29e4c6503893] ##############  Photos data: [{"filename"=>"fd110c8b8c205bfa1b3b064ad0ee9ed9.jpg", "ingredient_id"=>"97"}, {"filename"=>"829217fbf3d58d69de8acc35ec3f77ba.jpg", "ingredient_id"=>"94"}]
2024-06-27T10:35:46.280020+00:00 app[worker.1]: I, [2024-06-27T10:35:46.279997 #2]  INFO -- : [ActiveJob] [AssociatePhotosJob] [f0778fa1-e2b8-41ca-91cf-29e4c6503893] ##############  Checking directory existence: /app/tmp/uploads/2
2024-06-27T10:35:46.280047+00:00 app[worker.1]: E, [2024-06-27T10:35:46.280028 #2] ERROR -- : [ActiveJob] [AssociatePhotosJob] [f0778fa1-e2b8-41ca-91cf-29e4c6503893] ##############  Directory does not exist: /app/tmp/uploads/2
2024-06-27T10:35:46.281183+00:00 app[worker.1]: I, [2024-06-27T10:35:46.281110 #2]  INFO -- : [ActiveJob] [AssociatePhotosJob] [f0778fa1-e2b8-41ca-91cf-29e4c6503893] Performed AssociatePhotosJob (Job ID: f0778fa1-e2b8-41ca-91cf-29e4c6503893) from Sidekiq(default) in 37.46ms
2024-06-27T10:35:46.282366+00:00 app[worker.1]: I, [2024-06-27T10:35:46.282307 #2]  INFO -- sentry: [Transport] Sending envelope with items [transaction, client_report] ee732149c355405588380b72f3b44131 to Sentry
2024-06-27T10:35:46.283312+00:00 app[worker.1]: pid=2 tid=1okm class=AssociatePhotosJob jid=ac84b420282a6df91872f1e2 elapsed=0.039 INFO: done
2024-06-27T10:35:46.568304+00:00 app[web.1]: I, [2024-06-27T10:35:46.568211 #2]  INFO -- : [a57b9326-db06-4f93-b7f6-a22772909aba]   Rendered ingredients/index.html.erb within layouts/application (Duration: 130.3ms | Allocations: 54504)
2024-06-27T10:35:46.611448+00:00 app[web.1]: I, [2024-06-27T10:35:46.611347 #2]  INFO -- : [a57b9326-db06-4f93-b7f6-a22772909aba]   Rendered layout layouts/application.html.erb (Duration: 173.4ms | Allocations: 67800)
2024-06-27T10:35:46.611678+00:00 app[web.1]: I, [2024-06-27T10:35:46.611644 #2]  INFO -- : [a57b9326-db06-4f93-b7f6-a22772909aba] Completed 200 OK in 183ms (Views: 115.1ms | ActiveRecord: 62.0ms | Allocations: 70203)
2024-06-27T10:35:46.616105+00:00 app[web.1]: I, [2024-06-27T10:35:46.616026 #2]  INFO -- sentry: 

Logs in dev environment on local machine when I clic on associate :

024-06-27T08:59:55.593Z pid=27250 tid=2ny class=ActiveStorage::AnalyzeJob jid=dafd2604d2e1e81d599c82cb INFO: start
2024-06-27T08:59:55.595Z pid=27250 tid=2hi class=AssociatePhotosJob jid=31d7031ada10569c22cff418 INFO: Enqueued ActiveStorage::AnalyzeJob (Job ID: b862e0ed-d028-4dc7-bbc8-9c7de66ca353) to Sidekiq(default) with arguments: #<GlobalID:0x00007fb012ec5b98 @uri=#<URI::GID gid://gestion/ActiveStorage::Blob/522>>
2024-06-27T08:59:56.428Z pid=27250 tid=2hi class=AssociatePhotosJob jid=31d7031ada10569c22cff418 INFO:   Cloudinary Storage (781.9ms) Deleted file from key: 8aw1ytl9zy7yc7j3s7mxchsletp1
2024-06-27T08:59:58.762Z pid=27250 tid=2hi class=AssociatePhotosJob jid=31d7031ada10569c22cff418 INFO:   Cloudinary Storage (2296.5ms) Uploaded file to key: 64v6cs5m6zxxtukhn4jw9dccet65 (checksum: EtNII0egV28Jr8KCPYFZLw==)
2024-06-27T08:59:58.764Z pid=27250 tid=2h6 class=ActiveStorage::AnalyzeJob jid=e4d49d45a9bfc311b5d639dc INFO: start
2024-06-27T08:59:58.765Z pid=27250 tid=2hi class=AssociatePhotosJob jid=31d7031ada10569c22cff418 INFO: Enqueued ActiveStorage::AnalyzeJob (Job ID: 3aee7198-a22a-45f6-a2f8-6fbcfc5ed054) to Sidekiq(default) with arguments: #<GlobalID:0x00007fb012d40a70 @uri=#<URI::GID gid://gestion/ActiveStorage::Blob/523>>
2024-06-27T08:59:58.787Z pid=27250 tid=2hi class=AssociatePhotosJob jid=31d7031ada10569c22cff418 INFO: Performed AssociatePhotosJob (Job ID: 248a1e9d-c02f-4b9b-8b96-2ede80242603) from Sidekiq(default) in 5745.35ms
2024-06-27T08:59:58.791Z pid=27250 tid=2hi class=AssociatePhotosJob jid=31d7031ada10569c22cff418 elapsed=6.16 INFO: done

Thank a lot

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật