In my Rails app I have a module that takes several images, and using RMagick "stitches" them together into a new single image. I'm able to successfully create the final image, but I'm having trouble saving this new image as an attachment to a model (using CarrierWave). The method that does the stitching looks like this:
def generate_collage(params)
final_image = ImageList.new
# ... code that puts together the composite image ...
return final_image.append(true).to_blob { |attrs| attrs.format = 'JPEG' }
end
I've got my User model with an uploader mounted:
class User < ActiveRecord::Base
mount_uploader :image, UserImageUploader
end
In the CarrierWave documentation under the ActiveRecord section, they show how to assign a new image, but they assume the file already exists somewhere. In my case it doesn't yet exist on the filesystem, and I'm outputting a blob... is there any way to go from that blob to generating an image upload for CarrierWave?
I suppose I'm trying to avoid saving this image temporarily into "#{Rails.root}/tmp/" and then reading it from there... it seems like I could cut out this step and send directly to CarrierWave somehow, but I don't know how! Is it possible?
I'm working on something similar right now. This should be possible, but an easy workaround is to save it to a temp file:
temp_file = Tempfile.new([ 'temp', '.png' ])
image.write(temp_file.path)
user = User.new
user.avatar = temp_file
user.save
temp_file.close
temp_file.unlink
I'm hoping to try to improve it to remove the file system dependency completely, by following the advice in one of these answers: How to handle a file_as_string (generated by Prawn) so that it is accepted by Carrierwave?
Related
I am trying to seed multiple image attachments to a model. I have been using this link but I am still sort of stuck since what I aim to do differs a little since:
I am trying to attach multiple images to each object (which I seed) in the model
I want to retrieve these images from my S3 bucket and attach them to the objects (is this possible?)
Here's my seed.rb:
shirt = Item.create(name:"Basic Shirt",price:19.99)
skirt = Item.create(name:"Basic Skirt",price:29.99)
sweater = Item.create(name:"Basic Sweater",price:39.99)
kid_hood = Item.create(name:"Basic Kid Hoodie",price:19.99)
# somehow attach images here?
I am using the aws-sdk-s3 gem in order to connect Active Storage to my S3 bucket. Please tell me if any additional files are needed for viewing. I will happily edit this post to include it.
ActiveStorage work on plain byte streams, so you can download the file (using open-uri for instance) and assign the stream as the content of the attachment.
Assuming you have the following (adapt if different)
class Item < ApplicationRecord
has_one_attached :photo
end
you can have your seeds as:
require 'open-uri'
shirt = Item.create(name:"Basic Shirt",price:19.99)
shirt.photo.attach(io: open('your-s3-nonexpiring-url'), filename: 'foo.bar')
# ...
Just a note: as of Ruby 3.0, you will need to call URI.open instead of open. See the reference to open-uri here.
I am using CarrierWave with fog to upload my images to S3.
I have model Image that can represent images of different sizes and according to that needs to be saved in different folder.
For example, for image.jpg I could have two different uploaded versions that need to be saved as:
'images/large/image.jpg'
'images/small/image.jpg'
There could be arbitrary number of use cases and versions using minimagick can't cover them all.
So far I haven't been able to find solution. Does anyone know how to do this?
I've seen this question asked a few times so I'll write what my final solution is.
Instead of defining mount_uploader on model I decided to just use Uploader independently and save urls to records later.
Dynamically changing store_dir and filename can be accomplished like this
uploader = Uploader.new
uploader.define_singleton_method(:store_dir) do
'new_store_dir'
end
uploader.define_singleton_method(:filename) do
'new_filename'
end
uploader.store!(image)
Using this approach you can also define names with local variables or whatever you have available in controller.
Hopefully it helps someone else as well.
in order to change where uploaded files are put, just override the store_dir method:, for your case (reference link)
class Image < CarrierWave::Uploader::Base
storage :file
# this if you using use condition for folder location
def store_dir
if model.somefield == "somecondition
"uploads/#{model.somefield}"
elsif model.somefield == "somecondition
"uploads/location2"
else
"uploads/default_dir"
end
end
# this is if you want to use version_name
def store_dir
'images/#{version_name}/'
end
end
I need to copy a file from one carrier wave object to another. They are different tables and different types of uploaders.
I started with:
user.avatar = image.content
(where user and image are model instances, avatar and content are the carrierwave mounted uploaders) which worked sometimes. It seems to work all the time locally, with a file storage, but intermittent when using fog and s3.
In a mailing list post I found this code:
user.avatar = image.content.file
that again worked sometimes.
My working solution so far is:
require "open-uri"
begin
user.avatar = open(image.url)
rescue Errno::ENOENT => e
begin
user.avatar = open(image.path)
rescue Errno::ENOENT => e
# Ok, whatever.
end
end
which is not only ugly, but fails to pass the extension validation because the opening of a remote file doesn't maintain the extension (jpg, png, etc.).
Perhaps one way you can do it is to set a remote image URL as per the Carrierwave gem documentation?
user.remote_avatar_url = image.url
From solutions discussed here I created simple CopyCarrierwaveFile gem to do this
usage is something like this:
original_resource = User.last
new_resource = User.new
CopyCarrierwaveFile::CopyFileService.new(original_resource, new_resource, :avatar).set_file
new_resource.save
nev_resource.avatar.url # https://...image.jpg
Here's a (albeit hacky) solution to that doesn't require an HTTP request to fetch the image:
module UploadCopier
def self.copy(old, new)
new.instance_variable_set('#_mounters', nil)
old.class.uploaders.each do |column, uploader|
new.send("#{column}=", old.send(column))
end
end
end
old_user = User.last
new_user = User.new
UploadCopier.copy(old_user, new_user)
new_user.save
I needed to copy a reference from one model to another model and I was successfully able to do so by doing the following:
my_new_model.update_column('attachment', my_other_model.attributes["attachment"]);
In this scenario, I did not care to actually make a copy of the file, nor did I care that 2 records were now linked to the same file (my system never deletes or modifies files after uploaded).
This may be useful to anyone who wants to just copy the reference to a file from one model to another model using the same uploader.
You can do this by copying files.
store_path is a carrierwave method from Uploader class. It returns uploaded file's folder relative path.
this clone file method should be called after model record is saved.
If record not saved, store_path may return wrong path if you specify store_dir with model id in uploader.
def clone_carrierwave_file(column_name)
origin_files = Dir[File.join(Rails.root, 'public', original_record.send(column_name).store_path, '*')]
return if origin_files.blank?
new_file_folder = File.join(Rails.root, 'public', send(column_name).store_path)
FileUtils.mkdir new_file_folder if !Dir.exist? new_file_folder
FileUtils.cp(origin_files, new_file_folder)
end
Hope it works.
I just wanted to copy an avatar reference from one object to another, and what worked for me was:
objectB.avatar.retrieve_from_store!(objectA.avatar.identifier)
objectB.save
I'm using carrierwave in a rails 3 application to upload and store a file from a remote source in my server's file system. I've got a setup that's totally standard, with an uploader mounted on the model that the image is associated with.
It works perfectly 99.9% of the time, but every 600th graph or so I run into an issue where the app persistently fails to serve the stored image. If I check on the filesystem, the graph image has been uploaded and stored in the correct location, with the correct file permissions and everything, but rails is totally unaware of it and continues to serve the default graph image for that instance.
In other words a graph with id 123 has it's image stored at /uploads/graphs/123/graph.png ... the correct image is there but as far as rails is concerned it has no image stored. All the other graph images still work fine, but I can't get rails aware of the image stored for 123. Removing and re-storing the image doesn't work. Manually removing the image and re-uploading doesn't work. I'm totally lost. The graph instance is valid, no errors in the logs when I save.
for example, in the console:
g = Goal.find_by_id("123")
g.remote_graph_url = "http://image.source/url.png"
> "http://image.source/url.png"
g.save
> true
g.graph?
> false
g.graph_url >> /default/image.png
here's the relevant code:
class GraphUploader < CarrierWave::Uploader::Base
def store_dir
"../uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def default_url
"/images/" + [version_name, "default_large_graph.png"].compact.join('_')
end
def extension_white_list
%w(jpg jpeg gif png)
end
def filename
"graph.png" if original_filename
end
end
class Graph
mount_uploader :graph, GraphUploader
end
Wish I could just reply...
So you are saving uploads outside of the public directory? Try removing the "../" before images, if that wasn't your intention.
I tried storing a local image in a rails console.
Because I have many pictures in my local storage (I use crawler to download tons of pictures), I want to store them into a database, with the benefit of paperclip to do some image job, like thumbnail etc.
If I use a webpage to save new pictures to database one by one, it will cost a lot of time. So I want to find a way in rails console (some code) that can batch save-picture-into-database.
To further clarify #andrea's answer:
YourPaperclippedModelHere.new(:your_paperclip_field => File.new(path, "r"))
So if your model is called Image and your paperclip field is data:
Image.new(:data => File.new(path_to_your_file, "r"))
If this is the model:
class User < ActiveRecord::Base
has_attached_file :avatar
end
then the following should work from the console:
>> User.create(:avatar => File.open('/path/to/image.jpg', 'rb'))
I dont know if it is what you want ... but
to save an paperclip asset from console
You could simple use a File instance .
a.e.
Image.new :data=>File.new("/path/to/image.jpg","r")
Late Answer but hopefully it will work for others.
You need to include.
File.new("#{Rails.root}/public/images/default_avatar.png", "r")