Using a Carrierwave custom process I'm not able to access the model.id (multi-page PDF) - ruby

The idea is, I want to convert a PDF to an image per page. I've tried other methods which are on Stackoverflow but to no avail. Below is my uploader file.
Uploader
class PdfUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
#//TODO - Remove converted images when deleting an entry, maybe? if needed?
storage :file
#//TODO - Add images into the same folder. model.id can't be accessed in a custom process.
def store_dir
"#{Rails.root}/public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def cache_dir
"#{Rails.root}/public/uploads/#{model.class.to_s.underscore}/#{mounted_as}/tmp"
end
def extension_white_list
%w(pdf)
end
def filename
super.chomp(File.extname(super)) + '.png'
end
version :pages do
process :create_pages
process convert: 'png'
end
process convert: 'png'
version :thumb do
process :cover
process :resize_to_fill => [200, 200, Magick::CenterGravity]
process convert: 'png'
end
def cover
manipulate! do |frame, index|
frame if index.zero?
end
end
def create_pages
manipulate! do |frame, index|
frame.write("#{store_dir}/#{index}.png")
end
end
end
The PDF will get converted to images for each page. But it will not recognize the model.id and store it in that folder. Instead it will store in the directory above that.
I've tried the adding the following to force the making of the folder, but it doesn't seem to know the model.id through a little bit of testing?
Tried to Make Dir
def create_pages
Dir.mkdir(store_dir) unless File.exists?(store_dir)
manipulate! do |frame, index|
frame.write("#{store_dir}/#{index}.png")
end
end
Help would be much appreciated, I've been stuck on this for a while and had to leave it for a couple of days out of frustration.
Thank you in advance.

This is most likely because you are saving your images with frame.write before the model itself is saved, and therefore also before an ID has been assigned.
Have a look at this answer for a potential solution: Carrierwave : error with model.id in the store_path

Related

Using an existing image on s3 with Carrierwave?

I'm trying to use existing images on my s3 and relate them to a carrierwave object. These images are stored in a different location from the store_dir of my uploader.
I currently have my migration setup using remote_image_url but it is extremely slow and I don't think re-uploading them is necessary.
So, is it possible to point my uploader to an existing image that uses a folder structure different from my store_dir?
Here's an example of what I have now.
example block from migration:
ActiveRecord::Base.connection.execute(update_images)
Models::Product.where('image IS NOT NULL').select(:id, :image).find_in_batches(batch_size: 500).with_index do |group, batch|
puts "Processing group ##{batch}"
group.each do |prod|
next unless prod[:image].present?
image = Models::ProductImage.new(product_id: prod[:id])
image.remote_image_url = prod[:image]
image.save
end
sleep(7)
end
uploader:
class ImageUploader < CarrierWave::Uploader::Base
storage :fog
def filename
"#{secure_token}.#{file.extension}" if original_filename.present?
end
def store_dir
"#{ENV['RACK_ENV']}/#{sub_folder}"
end
def cache_dir
"#{ENV['RACK_ENV']}/#{sub_folder}"
end
def extension_allowlist
%w(jpg jpeg gif png)
end
def size_range
1.byte..10.megabytes
end
def sub_folder
case model
when Models::UserImage
'user_images'
when Models::ProductImage
'product_images'
else
'misplaced'
end
end
protected
def secure_token
var = :"##{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
end
end

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

Manually upload and save files in Carrierwave

I have a directory of existing files which I need to migrate into my rails app as part of a legacy migration. Essentially I need to upload these files manually and save a new record for them in the database. I haven't quite found the proper way to do this. Currently I have the following in a rake task:
#attachments.each do |attachment|
begin
new_attachment = Attachment.new
#attachment_file_path = "/home/username/Attachments/" + attachment.Filename
file = File.open(#attachment_file_path)
new_attachment[:file] = new_attachment.file.store!(file)
# Map old record fields to new
new_attachment.attributes = {
:project_id => attachment.ProjectID,
:name => attachment.Description,
:user_id => attachment.UserId,
:created_at => attachment.CreatedDate,
:updated_at => attachment.LastModifiedDate
}
new_attachment.save!
puts "Attachment added successfully "
rescue => error
puts "Error migrating Attachment: #{error}"
end
end
attachment.rb
class Attachment < ActiveRecord::Base
mount_uploader :file, FileUploader
end
uploader:
class FileUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick
include CarrierWave::MimeTypes
process :set_content_type
storage :fog
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(jpg jpeg gif png pdf doc docx txt)
end
version :thumb do
process resize_to_fit: [152, nil]
end
def default_url
ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
end
protected
def image?(new_file)
if new_file.content_type == nil
return false
else
new_file.content_type.include? 'image'
end
end
end
This does not work currently. The file never gets uploaded, and occasionally I get the following error:
Failed to manipulate with rmagick, maybe it is not an image? Original Error: no decode delegate for this image format
In this instance, the file is a '.doc' file.
What is the correct way to open a local file and upload it manually via Carrierwave?
Any help is appreciated.
Try this
#attachments.each do |attachment|
begin
options = {
:project_id => attachment.ProjectID,
:name => attachment.Description,
:user_id => attachment.UserId,
:created_at => attachment.CreatedDate,
:updated_at => attachment.LastModifiedDate,
:file => File.new(File.join("/home/username/Attachments/",attachment.Filename))
}
new_attachment = Attachment.new(options)
new_attachment.save!
puts "Attachment added successfully "
rescue => error
puts "Error migrating Attachment: #{error}"
end
end
Perhaps that would do for you as carrierwave would internally call store! for you
Question?
Failed to manipulate with rmagick, maybe it is not an image? Original Error: no decode delegate for this image format
Not sure what are you trying to over here because you have define an image? method which is not specified in condition also is that something that you want the content_type to be only present for image file
if no perhaps only the process call would work
process :set_content_type
if yes then perhaps you have to do something like this
process :set_content_type , :if => :image?
def image?(new_file)
%w(jpg jpeg gif).include?(new_file.extension)
end
Hope this help
EDIT based upon the comment
try this just used the condition same logic
version :thumb ,:if => image? do
// your code
end

CarrierWave: Use of file name in store_dir

I'm saving images to S3 using CarrierWave. Before uploading the images are renamed to a random string. Now for faster lookups I wanted to put the files in directories named after the two first letters of the filenames. But doing so, Rails aborts with stack level too deep.
So I assume calling #{model.image[0, 2] leads to an infinite recursion. Is there a way to access just the file name as a string? Or an even simpler solution?
This is the uploader:
class ImageUploader < CarrierWave::Uploader::Base
storage :fog
def store_dir
"images/#{model.image[0, 2]}/"
end
def filename
"#{secure_token}.#{file.extension}" if original_filename.present?
end
protected
def secure_token
var = :"##{mounted_as}_secure_token"
model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.hex(8))
end
end
try the :file_identifier method
class ImageUploader < CarrierWave::Uploader::Base
...
def store_dir
"images/#{model.file_identifier[0, 2]}/#{model.id}"
end
...
end
The easy answer is to add a proxy attribute such as filename in the Image model and access that using read_attribute:
class Image < ApplicationRecord
mount_uploader :image, ImageUploader
def filename
read_attribute(:image)
end
...
end
Then in your ImageUploader#store_dir method make reference to the proxy attribute filename:
def store_dir
"images/#{model.filename}"
end
This worked for me and eliminated the recursive error problem.

Ruby: Paperclip, S3, and Deep-cloning

What I have is a Theme model, which contains many Assets. Assets are using Paperclip and storing their file content in my Amazon AWS-S3 system. I'm also using deep_clone because my customers have the ability to copy built in Themes and then modify them to their hearts content. All the deep_clone stuff is working great, but when I deep_clone the assets, the old file contents don't get added to my S3 buckets. The record gets saved to the database, but since the file-contents don't get saved with the new ID the file.url property points to a dead file.
I've tried calling paperclip's save and create method manually but I can't figure out how to get paperclip to "push" the file back to the bucket since it now has a new ID, etc....
require 'open-uri'
class Asset < ActiveRecord::Base
belongs_to :theme
attr_accessor :old_id
has_attached_file :file,
:storage => "s3",
:s3_credentials => YAML.load_file("#{RAILS_ROOT}/config/aws.yml")[RAILS_ENV],
:bucket => "flavorpulse-" + RAILS_ENV,
:path => ":class/:id/:style.:extension"
validates_attachment_presence :file
validates_attachment_size :file, :less_than => 5.megabytes
before_save :delete_assets_in_same_theme_with_same_name
after_create :copy_from_cloned_asset
private
def delete_assets_in_same_theme_with_same_name
Asset.destroy_all({:theme_id => self.theme_id, :file_file_name => self.file_file_name})
end
def copy_from_cloned_asset
if (!old_id.blank?)
if (old_id > 0)
old_asset = Asset.find(old_id)
if (!old_asset.blank?)
self.file = do_download_remote_image(old_asset.file.url)
self.file.save
end
end
end
end
def do_download_remote_image (image_url)
io = open(URI.parse(image_url))
def io.original_filename; base_uri.path.split('/').last; end
io.original_filename.blank? ? nil : io
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
Any ideas on how I can get paperclip to push the file? I also wouldn't be opposed to doing this using Amazon's aws-s3 gem but I couldn't seem to get that to work either.
According to this former question/answer, it should be possible with this simple line of code:
self.file = old_asset.file

Resources