save generated files with paperclip or carrierwave - ruby-on-rails

how can I save generated xls (without uploading form) file with paperclip or carriervawe to ActiveRecord and use it then to upload on amazon s3?
file = Tempfile.new(['conracts','.xlsx'])
ToXls::Writer.new(content, {:columns => [:name, :address],
:headers => ["name","address"]}).write_io(file)
file.rewind
this is the xls file I generate , but i need to save it permanent in order to upload it with delayed job on s3.

With Carrierwave, you can assign a normal file to the mountpoint of the uploader, then save the model.
Carrierwave will then handle all uploading.
So if you have a model
class Project < ActiveRecord::Base
mount_uploader :xml_file, XmlUploader
end
you can write
pro=Project.new
pro.xml_file = file # your generated tempfile
pro.save
I'm not familiar with paperclip.

Related

How to implement attachment versioning for files with same names? RoR 5.2

Project based on RoR 5.2. For attachments uploading I use Carrierwave gem and AWS S3 bucket as storage.
I need to implement versioning for uploaded files with same names.
I mean that file with the same name should not be replaced when I will upload a new one.
For example: model User has a mounted uploader for documents. If I will upload file with name «doc.docx» and after I will upload one else file for the same model object and with the same name, old file will be replaced with new one.
I have already investigated that I can generate token or add timestamp to get unique filename, but I need that if I will upload the new file with same name (for example 'doc.docx') for the same object, it shoild be renamed to 'doc(1).docx' or 'doc_1.docx'. And the new one will be renamed to 'doc(2).docx' or 'doc_2.docx', etc.
Versioning in the AWS panel for this bucket has already enabled.
Is there any way to achieve this? I'm ready to change from Carrierwave to Active Storage. But I have not found at least one case for this.
Any suggestions?
Thanks in advance!
I have some ideas about renaming. At first, we can split your problem:
Problem with file renaming
Problem with file versioning
I solved my problem with renaming with similar code (Carrierwave + video processing). Here I created thumbnail version for an uploaded video, with a specific name (write your custom function instead of png_name, you can check a number of previously uploaded files and etc):
# Create different versions of your uploaded files:
# ffmpegthumbnailer
version :thumb do
process thumbnail: [{ format: 'png', size: 360, logger: Rails.logger }]
def full_filename for_file
png_name for_file, version_name
end
end
version :preview do
process thumbnail: [{ format: 'png', size: 600, logger: Rails.logger }]
def full_filename for_file
png_name for_file, version_name
end
end
def png_name(for_file, version_name)
%Q{#{version_name}_#{for_file.chomp(File.extname(for_file))}.png}
end
And you can solve the problem with versioning by creating proxy-model. For instance, you have User model, than you create an association called Document. You can mount your uploader to the Document model, and use code above for the renaming.
class User < ApplicationRecord
has_many :documents, dependent: :destroy
def last_document
documents.last
end
end
class Document < ApplicationRecord
mount_uploader :file, DocumentUploader
validates_integrity_of :file
end

Report from backend to upload to s3 using carrierwave gem ruby

I have Axlsx::Package.new object which is used to create a excel file in backend and now i have to upload this excel file to s3 using carrierwave gem. I have either file path or Axlsx::Package.new object but in both case it is throwing file can't be blank.. I am not sure what I am doing wrong.
Please help me how can I achieve this.
class Attachment < ApplicationRecord
# CarrierWave
mount_uploader :file, AttachmentUploader
validates :file, presence: true
end
when I execute this line, where I don't have ActionDispath::FileUpload object as i am creating file in backend not from UI form. due to this I have
file = Axlsx::Package.new
file_path = "uploads/exports/download_request_#{current_datetime}.xlsx"
attachment = Attachment.create!(file: file, entity_type: self.class)
i tried with file also and file_path also but it is not working and throwing file can't be blank...
class Attachment < ApplicationRecord
# CarrierWave
mount_uploader :file, AttachmentUploader
validates :file, presence: true
end
it should save to s3 as how it is working in case when i upload file from UI form using input type file tag.
Please help

Migrating Carrierwave to ActiveStorage

I have an application using Carrierwave to handle file uploads but really love the simplicity of ActiveStorage. There are plenty of tutorials on migrating from Paperclip to ActiveStorage with the former sunsetting development, but I see nothing on migrating from Carrierwave to ActiveStorage. Has anyone successfully done the migration and could point me in the right direction?
The procedure is really simple actually.
step 1:
configure the active store bucket. try to use a different bucket than your carrierwave one
step 2:
configure your model in order to provide access to the ActiveStorage. example
class Photo < AR::Base
mount_uploader :file, FileUploader # this is the current carrierwave implementation. Don't remove it
has_one_attached :file_new # this will be your new file
end
Now you will have two implementations for the same model. carrierwave access at file and ActiveStorage at file_new
step 3:
download images from Carrierwave and save it to active storage
This can be implemented in a rake file, activeJob etc..
Photo.find_each do |photo|
begin
filename = File.basename(URI.parse(photo.fileurl))
photo.file_new.attach(io: open(photo.file.url), filename: d.file )
rescue => e
## log/handle your errors in order to retry later
end
end
At this point you will have one image on the carrierwave bucket and the newly created image on the active storage bucket!
(optional)
step 4
Once you are ready with the migration modify your model changing the active storage accessor and remove the carrierwave integration
class Photo < AR::Base
has_one_attached :file # we changed the atachment name from file_new to file
end
This is a convenience option so your integration in controllers and other places remain intact. hopefully!
 step 5
Update your records on active_storage_attachments table in order for the attachments be found as file and not file_new update column name from "file_new" to "file"
notes
Is possible to make some other tweaks to the application in order to handle things to consider
if your site will be running while you do the migration one way to fully operate would be implement active storage for new uploads then when you display images you can display active storage and carrierwave as a fallback
something like this in a helper:
photo.attached? ? url_for(photo.file_new) : photo.file.url
I hope this helps!
To begin with
You'll have to run this bundle exec rails active_storage:install
rails db:migrate
Replace mount_uploader :image, ImageUploader, to look like has_one_attached :image, in your model.
For rendering the image in the view, you should replace image_url with url_for(user.image).
You don't have to make any change to the controller code or in params, as the attribute image is already a strong parameter.
# user.rb
class User < ApplicationRecord
# mount_uploader :image, ImageUploader
has_one_attached :image
end
# show.html.erb
<%= image_tag url_for(user.image) %>
or
<%= image_tag user.image %>

Paperclip to upload images to S3 in Rails. The files upload at a very slow rate. A work-around?

I'm working on a rails app where the user will be uploading large quantities of images.
My current setup: Using SWFUpload to upload multiple files at once using the Paperclip plugin with S3 storage. After the original image is uploaded to S3, Delayed_Job is used for the post processing (thumbnails, etc).
The problem I have is that the images upload at a very slow rate. I'm assuming the default Paperclip setup is that the image will go from the user to -> my server to -> s3.
I was thinking that I can have the images upload directly to s3 but I'm not sure how to implement that with Paperclip and post processing. I couldn't find any plugins or examples dealing with this.
Does anyone have suggestions? If not, can you point me in the right direction?
Thanks in advance!
Tim
I've ran into this same problem a few times. The way I solved it was by creating 2 models, a Image model and a TempImage model, which inherits from the Image model. This requires you to have a type column on your Image table. The TempImage model saves the image locally, then when you access it from the Image model directly and resave it, it will follow whatever is defined in the Image model, being Amazon S3.
Example:
# Will save in the database as a TempImage inside the Image table
temp = TempImage.create(:asset => File.new('some_path', 'r'))
# When you find it again through the Image model, it bypasses the type column
# so next time you save it, it is saved as an Image.
amazon = Image.find(temp.id)
amazon.save!
Here is my delayed job:
class MoveToS3Job < Struct.new(:temp_revision_id)
def perform
upload = Image.find(temp_revision_id)
temp_path = File.expand_path("tmp/uploads/#{upload.asset_file_name}", Rails.root)
upload.asset = File.new(temp_path, 'r')
upload.save!
if File.exists?(temp_path) && !File.directory?(temp_path)
File.delete(temp_path)
end
rescue ActiveRecord::RecordNotFound
# If the record wasn't found, do some sort of
# error report, but don't keep it in the queue.
end
end
Here is the TempImage model:
class TempImage < Image
has_attached_file :asset, {
:path => ":rails_root/tmp/uploads/:basename_:updated_at.:extension"
}
end
Then the original Image model:
class Image < ActiveRecord::Base
# Validations
validates :asset, :presence => true
# Paperclip
has_attached_file :asset, :styles => {
:preview => ['100x100#', :png],
:thumb => ['50x50#', :png]
},
:default_style => :thumb,
:storage => :s3,
:bucket => 'bucket-name',
:s3_credentials => File.expand_path('config/s3.yml', Rails.root),
:path => "photos/:id_partition/:style.:extension"
end
Your original Image model should always contain your post processing, as that will be done in the background.
You can always overwrite some methods to make it a little cleaner, but this gives you a better idea of how it works and what you need to do to so you can have it work like you want it to.
If you end up going the route of uploading directly to S3 which offloads the work from your Rails server, please check out my sample projects:
Sample project using Rails 3, Flash and MooTools-based FancyUploader to upload directly to S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-FancyUploader
Sample project using Rails 3, Flash/Silverlight/GoogleGears/BrowserPlus and jQuery-based Plupload to upload directly to S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-Plupload
By the way, you can do post-processing with Paperclip using something like this blog post describes:
http://www.railstoolkit.com/posts/fancyupload-amazon-s3-uploader-with-paperclip

Access to paperclip temp file when using s3 storage option on heroku

I am using the Paperclip gem to resize upload photos and store them on amazon S3. I need access to the resized photo to also pass along to another web service, during the lifecycle of the upload request.
I suspect there is a temp file created somewhere the imagemagik uses before the photo is uploaded to s3. How can I get access to it.
According to Paperclip readme there're a few callbacks that it calls after and before processing.
For each attachment:
before_post_process
after_post_process
Only for a specific attachment:
before_[attachment]_post_process
after_[attachment]_post_process
I think in your case you should use one of the after callbacks to get the resized photo. Then you should be able to access the file with queued_for_write. For example:
class MyModel < ActiveRecord::Base
has_attached_file :photo, :styles => { :small => "300x300>" }
after_post_process :send_photo
private
def send_photo
path = photo.queued_for_write[:small].path
# upload the photo to the ws here
end
end

Resources