Can't upload image to Cloudinary from Sinatra - ruby

I'm trying to upload an image to Cloudinary from within my Sinatra web app. I
got it saving the image locally by using CarrierWave and I know Cloudinary and
CarrierWave play nicely with each other but I've tried everything and no dice.
I've followed the docs but they are very Rails-centric. Damn you Rails!
# Model - User.rb
class ImageUploader < CarrierWave::Uploader::Base
include Cloudinary::CarrierWave
process convert: 'png'
version :face do
cloudinary_transformation gravity: :face
end
end
class User
include DataMapper::Resource
attr_accessor :image
property.............
mount_uploader :image, ImageUploader
end
# Config - app.rb
require 'carrierwave/datamapper'
require 'cloudinary'
# DataMapper code ...
# Some routes ...
post '/add' do
user = user.create(name: params[:name])
Cloudinary::Uploader.upload(params[:image], api_key: 'API_KEY', api_secret: 'API_SECRET', cloud_name: 'CLOUD_NAME')
# View (haml)
%head
%script{src: "//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"}
%script{src: "js/cloudinary_js/jquery.ui.widget.js"}
%script{src: "js/cloudinary_js/jquery.iframe-transport.js"}
%script{src: "js/cloudinary_js/jquery.fileupload.js"}
%script{src: "js/cloudinary_js/jquery.cloudinary.js"}
# snip snip ...
%form{action: "/add", method: "POST", enctype: "multipart/form-data"}
%input{type: "file", name: "image"}
%button Submit
The error I'm getting when trying to submit the form is:
TypeError at /add
no implicit conversion of Hash into String
The line it gives me that is causing the error is the Cloudinary uploader line in the post method.
Here's my params[:image] hash
{:filename=>"batman_robin.jpg", :type=>"image/jpeg", :name=>"image", :tempfile=>#<Tempfile:/var/folders/rv/2w8gpstn4hb2lb__27n27ksh0000gn/T/RackMultipart20131223-36484-tgmaba>, :head=>"Content-Disposition: form-data; name=\"image\"; filename=\"batman_robin.jpg\"\r\nContent-Type: image/jpeg\r\n"}
In the docs it mentions a file signature then states that CarrierWave handles this. I think my problem lies with the upload form.
Where am I going wrong with this? Anyone know what 'cl_image_upload_tag' generates and what are Cloudinary's "special attributes" that I need to add to my file upload tag?
UPDATE: So I figured that and I can actually just pass the image directly like so:
Cloudinary::Uploader.upload(params[:image][:filename] ...
However it now says
No such file or directory - batman_robin.jpg
which is strange. I've hardcoded a URL in there and it gets uploaded to Cloudinary. Any ideas? Also, any idea about the Cloudinary jQuery upload?

params[:image] is a hash but Cloudinary::Uploader.upload expects a string as the first argument. All the examples in the docs give a name or a URL, so pass the :filename from the params, e.g.
post '/add' do
user = user.create name: params[:name]
image_meta = params[:image]
filename = image_meta.delete :filename
Cloudinary::Uploader.upload filename, {api_key: 'API_KEY', api_secret: 'API_SECRET', cloud_name: 'CLOUD_NAME'}.merge(image_meta)
I'd also put all those default settings into a settings hash (and have them read in from the environment if they're secret), e.g.
config do
set :cloudinary_api, {api_key: ENV['API_KEY'],
api_secret: ENV['API_SECRET'],
cloud_name: ENV['CLOUD_NAME']}
end
then the call becomes…
Cloudinary::Uploader.upload filename, settings.cloudinary_api.merge(image_meta)

Instead of the filename you need to pass the actual content which is hold in Tempfile So passing Cloudinary::Uploader.upload(params[:image][:tempfile]) should work.

Related

Zero Bytes Sent When Sending Axlsx Gem Generated File From Sinatra

Attempting to prompt a download window and stream an XLSX file using Ruby Sinatra and the AXLSX gem, my excel file serializes successfully to local file, so I know its a valid excel doc, but I need it to transfer content to the end user. There haven't been any docs online with examples of AXLS and Sinatra used together, only rails. Help is appreciated!
class Downloads < Sinatra::Base
get '/downloads/report' do
## ...
Axlsx::Package.new do |p|
p.workbook.add_worksheet(name: 'tab name') do |sheet|
## ...
end
content_type 'application/xlsx'
attachment 'cost-code-dashboard.xlsx'
p.to_stream # unsuccessful
# p.to_stream.read # unsuccessful as well
end
end
end
I have also tried the following snippet unsuccessfully
Axlsx::Package.new do |p|
## ...
send_file p.to_stream.read, type: "application/xlsx", filename: "cost-code-dashboard.xlsx"
end
It appears that the issue had everything to do with how Axlsx::Package.new was called, the helper functions were not available inside Axlsx, the following solution worked - online documentation said that the below content_type was better
get '/downloads' do
content_type :'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
p = Axlsx::Package.new
p.workbook.add_worksheet(name: 'Test') do |sheet|
sheet.add_row ['Hello world']
end
p.to_stream
end

Attach images in seed.rb file in rails using active storage

I have a class called vehicles which can have an image attached to it. I have made a default image to display if no other image was uploaded in the vehicles.rb file.
I would like to include images in the seed.rb file so I don't have to manually upload all images. Is this possible?
Help is much appreciated thanks.
Here is my vehicle.rb:
class Vehicle < ApplicationRecord
belongs_to :make
belongs_to :model
accepts_nested_attributes_for :make
accepts_nested_attributes_for :model
has_one_attached :image
after_commit :add_default_image, on: %i[create update]
def add_default_image
unless image.attached?
image.attach(
io: File.open(Rails.root.join('app', 'assets', 'images', 'no_image_available.jpg')),
filename: 'no_image_available.jpg', content_type: 'image/jpg'
)
end
end
end
Here is how i am creating records in my seed file but I would like to include the images as well:
v = Vehicle.create(
vin: '1QJGY54INDG38946',
color: 'grey',
make_id: m.id,
model_id: mo.id,
wholesale_price: '40,000'
)
You could use the Ffaker gem to easily generate fake data and finally after creating the vehicle record you can update your record image attribute from the instance variable. Check Attaching File/IO Objects
This would be the code of db/seed.rb file:
if Vehicle.count.zero?
10.times do
v = Vehicle.create(
vin: FFaker::Code.ean,
color: FFaker::Color.name,
maker_id: m.id,
model_id: m.id,
wholesale_price: '40,000'
)
v.image.attach(
io: File.open(File.join(Rails.root,'app/assets/images/photo.jpg')),
filename: 'photo.jpg'
)
end
end
Don't forget to add the ffaker gem to your Gemfile file.
As the above answer states it's in the Edge Guides, for people having trouble getting the path right, this is an example of a line in the seeds.rb file attaching an avatar to the 1st created user:
User.first.avatar.attach(io: File.open(File.join(Rails.root,'app/assets/images/avatar.jpg')), filename: 'avatar.jpg')
This approach worked for me on Rails 7:
I put my images in public/images folder.
Seed the DB
db/seeds.rb
post = Post.create!(title: "My Title", description: "My description")
post.image.attach(io: File.open(Rails.root.join("public/images/sample.jpg")), filename: "sample.jpg")
Then in my views:
app/views/posts/show.html.erb
# to get the image URL:
polymorphic_url(#post.image)
# to display the image
image_tag post.image if post.image.attached?
All this assuming you installed ActiveStorage:
In your terminal:
$ rails active_storage:install
$ rails db:migrate
Then in your model:
models/post.rb
has_one_attached :image
And in your controller. Add image to permitted params:
params.require(:post).permit(:image, :everything_else)

Where does CarrierWave stores uploads

Please help me understand how does CarrierWave works.
I'm using minimal Sinatra/DataMapper app with following contents:
class VideoUploader < CarrierWave::Uploader::Base
storage :file
end
class Video
include DataMapper::Resource
property :id, Serial
property :name, String
property :desc, Text
mount_uploader :file, VideoUploader
end
get '/' do
slim :form
end
post '/' do
video = Video.new
video.name = params[:name]
video.desc = params[:desc]
video.file = params[:file]
video.save
redirect '/'
end
As I understood mount_uploader :file, VideoUploader string in Video definition adds .video method to Video instance, and I can store uploads assigning params[:file] to it. When I'm trying to send form from browser, the request successfully creates record in DB table, but I can't find any signs of file existence either in DB and public_directory. What I'm doing wrong?
You probably should define the store_dir method inside the VideoUploader class:
class VideoUploader < CarrierWave::Uploader::Base
storage :file
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
....
end

Paperclip in Rails 4 - Strong Parameters Forbidden Attributes Error

Having a problem with a Paperclip upload in Rails 4 - failing on ForbiddenAttributesError (strong parameters validation). Have the latest paperclip gem and latest rails 4 gems.
I have a model "Image" with an attached file "upload" in the model:
has_attached_file :upload, :styles => { :review => ["1000x1200>", :png], :thumb => ["100x100>", :png]}, :default_url => "/images/:style/missing.png"
The image model was created with a scaffold, and I added paperclip migrations. The form partial was updated to use
f.file_field :upload
the form generates what appears to be a typical set of paperclip params, with the image param containing the upload. I am also passing a transaction_id in the image model, so it should be permitted. But that's it - the image and the transaction ID.
I expected to be able to write the following in my controller to whitelist my post - but it failed:
def image_params
params.require(:image).permit(:transaction_id, :upload)
end
So I got more explicit - but that failed too:
def image_params
params.require(:image).permit(:transaction_id, :upload => [:tempfile, :original_filename, :content_type, :headers])
end
I'm a bit frustrated that Rails 4 is not showing me what ForbiddenAttributesError is failing on in a development environment - it is supposed to be showing the error but it does not - would be a nice patch to ease development. Or perhaps everyone else is getting something that I am missing! Thanks much for the help.
I understand what happened here now - and will leave this up in the hope it helps someone else. I was porting code from a rails 3 project and missed the line that created the image:
#image = current_user.images.new(params[:image])
In rails 4 this is incorrect (I beleive). I updated to
#image = current_user.images.new(image_params)
and that solved my problem.
It looks like your first one should have worked. This is what I use for my projects.
class GalleriesController < ApplicationController
def new
#gallery = Gallery.new
end
def create
#user.galleries.new(gallery_params)
end
private
#note cover_image is the name of paperclips attachment filetype(s)
def gallery_params
params.require(:gallery).permit(:cover_image)
end
end

Uploading a simple CSV file to a controller

I'm trying to upload a CSV file to my controller and access the data within.
Here's the code for the Controller:
class DatabaseImporterController < ApplicationController
def index
end
def import
# Receive the uploaded CSV file and import to the database.
csv_file = params[:csv_file]
logger.debug { csv_file.inspect }
#CSV.foreach("parse.csv") do |row|
#end
render :template => "database_importer/index"
end
end
And the ouput of logger.debug:
{"utf8"=>"✓",
"authenticity_token"=>"Z4+XlmrcH+8JPL6Yq52ymVOMfiGEI9mN8LuoxoBLp8M=",
"csv_file"=>#<ActionDispatch::Http::UploadedFile:0x007feca81b3fb8 #original_filename="Install-Linux-tar.txt", #content_type="text/plain", #headers="Content-Disposition: form-data; name=\"csv_file\"; filename=\"Install-Linux-tar.txt\"\r\nContent-Type: text/plain\r\n", #tempfile=#<File:/tmp/RackMultipart20121229-10294-1ngf634>>,
"commit"=>"Import CSV Car Database",
"controller"=>"database_importer",
"action"=>"import"}
Accoding to the documentation on the official Ruby on Rails page:
The object in the params hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file.
As I understand it, the uploaded file is somewhere in my disc (in my Heroku instance) and I can access it temporarily.
How can I access the file? I've tried the following and I get and error message:
csv_file = params[:csv_file][:tempfile] # This is how I try to get the file page of the temporary file.
undefined method `[]' for #<ActionDispatch::Http::UploadedFile:0x007fecb02103c8>
You want to call tempfile, not [:tempfile]:
params[:csv_file].tempfile
You can see all the methods available in the docs for ActionDispatch::Http::UploadedFile.

Resources