Store uploaded files carrierwave RoR - ruby-on-rails

i am trying to upload a file with carrierwave in my rails app and currently this is my code:
Controller:
def fileSave
#code.store!(code)
end
View:
= form_for #code = Code.new(params[:code]), :as => :code, :html => {:multipart => true} do |f|
div class="browse"
span
= f.file_field :code
= f.submit 'Upload'
Uploader:
# encoding: utf-8
class CodeUploader < CarrierWave::Uploader::Base
def pre_limit file
#require 'debugger'; debugger
if file && file.size > 100.megabytes
raise Exception.new('too large')
end
true
end
storage :file
def store_dir
"public/uploads"
end
def extension_white_list
%w(txt js ttf html)
end
def filename
"file.txt" if original_filename
end
end
Model:
require 'carrierwave/orm/activerecord'
class Code < ActiveRecord::Base
attr_accessor :code
mount_uploader :code, CodeUploader
end
And my problem is i can not store the uploaded file. x[ I am sure this is like 3 lines of code but i can not figure it out. Also the file to be uploaded is expected to be txt (probably figured that out looking the extension list).
Thanks to all readers and answerers. :}
P.S. I was wondering if i could create some kind of imaginary file, a file which is not really created. The is idea is if a take a text from a textarea and create a file (the imaginary one), store the text inside and then eventually save the whole file (maybe use carrierwave as and manually store it).

Related

Rails tag_id3v1 mp3

Rails 4.2 ap and taglib-ruby gem
I have tried a few approaches to tag my audio files using taglib-ruby.
I have tried as a carrierwave process, and now, I am trying to tag after_save.
My question, if I am doing a callback after_save:
def tag_id3v1(tags)
TagLib::MPEG::File.open(file.path) do |file|
tag = file.id3v1_tag(true)
tag.title = :title
file.save
end
end
what should my file path be? I have tried :file, :file_name, and the uploader version url, #{track.mp3.url}
I am trying to reopen the saved files and write the tags to the files. Does anyone have any hints on the best approach to do this?
Finally
TagLib::MPEG::File.open(file.file) do |file|
Always something like, "file.file". No matter the approach, that is what did the trick for me.
I ended up doing this in a carrierwave callback inside of a :version. Final code
version :mp3 do
process :convert => [:mp3]
def full_filename(for_file)
"#{super.chomp(File.extname(super))}.mp3"
end
after :store, :tag_id3v2
end
def tag_id3v2(for_file)
TagLib::MPEG::File.open(file.file) do |file|
tag = file.id3v2_tag(true)
tag.title = "#{model.title}"
file.save
end
end
(...)

Store two versions of a file at once using Carrierwave

I'm using Carrierwave and Fog gems to store a file to my Amazon S3 bucket (to /files/file_id.txt). I need to store a slightly different version of the file to a different location in the bucket (/files/file_id_processed.txt) at the same time (right after the original is stored). I don't want to create a separate uploader attribute for it on the model - is there any other way?
This my current method that stores the file:
def store_file(document)
file_name = "tmp/#{document.id}.txt"
File.open(file_name, 'w') do |f|
document_content = document.content
f.puts document_content
document.raw_export.store!(f)
document.save
end
# I need to store the document.processed_content
File.delete(file_name) if File.exist?(file_name)
end
This is the Document model:
class Document < ActiveRecord::Base
mount_uploader :raw_export, DocumentUploader
# here I want to avoid adding something like:
# mount_uploader :processed_export, DocumentUploader
end
This is my Uploader class:
class DocumentUploader < CarrierWave::Uploader::Base
storage :fog
def store_dir
"files/"
end
def extension_white_list
%w(txt)
end
end
This is how my final solution looks like (kinda) - based on Nitin Verma's answer:
I had to add a custom processor method for the version to the Uploader class:
# in document_uploader.rb
...
version :processed do
process :do_the_replacements
end
def do_the_replacements
original_content = #file.read
File.open(current_path, 'w') do |f|
f.puts original_content.gsub('Apples','Pears')
end
end
considering that you need similar file but with different name.
for this you need to create a version for file in uploader.
version :processed do
process
end
and now second file name will be processed_{origional_file}.extension. if you want to change file name of second file you can use this link https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Customize-your-version-file-names

Link existing S3 file as paperclip attachment

I have an external service that creates files and stores them into S3 (that my Rails app has access too).
I want to be able to use Paperclip in order to store that S3 object, as well as a thumbnail of it. I haven't been able to find documentation on how to use Paperclip with files that are already on S3. So, my code should look like:
page = Page.find(3)
page.s3_url = s3_url # I have the s3 route. And I would need to generate a thumbnail too.
page.save
So, in other words:
How can I tell PaperClip that my attachment is already in S3?
EDIT:
Here is what I have so far:
I know the file_name, the content_length and the content_type of the file that is already uploaded in S3. So, since the association is a Page has_attached_file :screenshot
This is what I do:
#page = Page.find(3)
#page.update_attributes(screenshot_content_type: screenshot_content_type, screenshot_file_size: screenshot_file_size, screenshot_update_at: screenshot_updated_at, screenshot_file_name: screenshot_file_name)
So, now I can do:
#page.screenshot and I see the paperclip object. However, when I do:
#page.screenshot.url => The url is not the one that I originally stored the image.
I've managed to solve this problem with paperclip interpolations:
Define in your model a field, witch would store path to the uploaded files
Load the paperclip attachment info to DB through property update
With interpolations specify a paperclip attachment :path, so it first looks for uploaded file, and then for default
Profit!
This will do the trick, because of how S3 storage composes urls:
path: This is the key under the bucket in which the file will be stored. The URL will be constructed from the bucket and the path. This is what you will want to interpolate. Keys should be unique, like filenames, and despite the fact that S3 (strictly speaking) does not support directories, you can still use a / to separate parts of your file name.
Here are more details with code:
Model
class MyModel
has_attached_file :file, path: ":existent_file_or_default", storage: :s3
end
Paperclip interpolations
Put this under config/initializers
Paperclip.interpolates :existent_file_or_default do |attachment, style|
attachment.instance.existent_file_path ||
attachment.interpolator.interpolate(":class/:attachment/:id_partition/:style/:filename", attachment, style)
end
Attach existent items
MyModel.create({
existent_file_path: "http://your-aws-region.amazonaws.com/your-bucket/path/to/existent/file.jpeg",
file_file_name: "some_pretty_file_name.jpeg",
file_content_type: "image/jpeg",
file_file_size: 123456
})
S3
Paperclip S3 integration is actually relatively simple - you'll be best looking at this Railscast on how to use Paperclip, and the afore-linked documentation to give you an idea on how Paperclip works; then you can just connect it to S3
--
In short, Paperclip basically takes a file object & saves the relevant data to your db. The storage of the file is dependent on the service you associate with Paperclip
What I'm trying to say is the two elements (data allocation & file storage) are two different elements of the gem, and if you can get Paperclip to handle the inbound files, you're half-way there:
Paperclip
Default implementation:
#app/models/page.rb
Class Page < ActiveRecord::Base
has_attached_file :photo
end
This will allow you to save an "attachment" to your Page database:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def new
#page = Page.new
end
def create
#page = Page.new(page_params)
end
private
def page_params
params.require(:page).permit(:title, :other, :information, :photo)
end
end
#app/views/pages/new.html.erb
<%= form_for #page do |f| %>
<%= f.file_field :photo %>
<%= f.submit %>
<% end %>
This will handle Paperclip uploads directly to your own app (storing the files in the public/system directory)
In order to get it to work with S3, you just have to add the S3 credentials to the model (taken from the Paperclip documentation):
#app/models/page.rb
Class Page < ActiveRecord::Base
has_attached_file :photo,
:storage => :s3,
:s3_credentials => Proc.new{|a| a.instance.s3_credentials }
def s3_credentials
{:bucket => "xxx", :access_key_id => "xxx", :secret_access_key => "xxx"}
end
end
Path
If you'd like to call the "S3" filepath for your file - you'll have to do this:
#app/controllers/pages_controller.rb
Class PagesController < ApplicationController
def show
#page = Page.find 3
#page.photo.url #-> yields S3 path ;)
end
end

rails carrierwave_direct unable to render direct_upload_form_for form

I'm try to set up carrierwave_direct for some image uploading in my rails 3.2 application but seem unable to render the form by using the direct_upload_form_for tags.
I'm getting the error "undefined method `direct_fog_url' for #< PostmarkerImage:0x007fdbe07b39f0 >" when loading the page containing the form.
PostmarkerImage is the model in which I've mounted the uploader like so:
class PostmarkerImage < ActiveRecord::Base
attr_accessible :image, :image_cache
belongs_to :postmarker
validates :image, :presence => true
mount_uploader :image, PostmarkerImageUploader
end
The image column is a string and I've also ensured that the migration has been run to create the column. The uploader now looks like this:
class PostmarkerImageUploader < CarrierWave::Uploader::Base
include CarrierWaveDirect::Uploader
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
# Include the Sprockets helpers for Rails 3.1+ asset pipeline compatibility:
include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
# Choose what kind of storage to use for this uploader:
# storage :file
# storage :fog
include CarrierWave::MimeTypes
process :set_content_type
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# # For Rails 3.1+ asset pipeline compatibility:
# # asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
#
# "/images/fallback/" + [version_name, "default.png"].compact.join('_')
# end
# Process files as they are uploaded:
# process :scale => [200, 300]
#
# def scale(width, height)
# # do something
# end
# Create different versions of your uploaded files:
# version :thumb do
# process :scale => [50, 50]
# end
# Add a white list of extensions which are allowed to be uploaded.
# For images you might use something like this:
def extension_white_list
%w(jpg jpeg gif png)
end
# Override the filename of the uploaded files:
# Avoid using model.id or version_name here, see uploader/store.rb for details.
# def filename
# "something.jpg" if original_filename
# end
end
I've also ensured that carrierwave_direct, fog, mini_magick and Imagemagick have already been installed. May I know what else I have missed out? It almost seems like the mounting of the uploader has not been done properly.
Managed to figure it out :) Just in case anyone has this problem:
I am implementing a separate model to hold my images instead of adding a column to an existing table. So the direct_upload_form_for expects the column (e.g. Image.new.image) instead of the Object (e.g. Image.new) which I was returning. I guess I was thrown off by the form_for part which usually expects an Object!
For anyone else who runs into this error, you are likely not passing the correct object to direct_upload_form_for. Let's say we had a User model with an avatar column. It should look like.
Model:
class User < ActiveRecord::Base
mount_uploader :avatar, AvatarUploader
end
Controller:
def edit # Could be any action, but I always direct upload to existing objects
#uploader = #user.avatar
#uploader.success_action_redirect = edit_registration_url(resource)
end
View (erb):
<%= direct_upload_form_for #uploader do |f| %>
<%= f.file_field :image %>
<% end %>
Notice how the #uploader object is actually the column #user.avatar, not the object #user. That's what the OP is talking about in his answer. It's just more confusing in his case because the object is not a User, it's a PostmarkerImage, which I assume is a model that only exists to hold images. This is common when you want one object (ie. a Postmarker) to have multiple images. So you create a model only for images, and set up a has_many relationship between them.
Pitfall: I was also getting this error a lot in production because I was forgetting to copy the code in the controller from the edit action to the update action. If the upload fails due to validation (like file type), you need to remember to set #uploader again! Similarly, if you have your uploader in new, remember to assign #uploader in create, as well.

Carrierwave Mongoid validation of file

I cant figure out how to validate that carrierwave has uploaded a document to my mongoid object.
i have a Document Class
class Content::Document < Content
mount_uploader :attachment, DocumentUploader
field :attachable_id
field :attachable_type
end
and an uploader:
require 'carrierwave/orm/mongoid'
class DocumentUploader < CarrierWave::Uploader::Base
storage = :filesystem
include CarrierWave::RMagick
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
def extension_white_list
%w(doc docx xls xlsx ppt pptx pdf )
end
i would like to validate that the upload exists and that it matches the white list else through a standard validation error
this is on Rails 2.3.8
While it's true that Carrierwave does have extensive tests, you could test for validity with something like this:
it "is valid with valid attributes" do
file_bytes = File.open("spec/binary/avatar.png")
valid_attrs = {:name => "foo", :description => "bar", :avatar => file_bytes}
user = User.new(valid_attrs)
user.should be_valid
end
Hope that helps!
In general you don't need to do that since this behaviour is already tested in carrierwave specs itself.
You can test your uploaders in isolation, using Carrierwave test helpers. E.g. I would just write a spec like
attachment_uploader.extension_white_list.should =~ %w(doc docx xls xlsx ppt pptx pdf)
But if you insist on testing that I would suggest using FakeFS to stub filesystem and then check with
File.exists? document.attachment.current_path
whether attachment has been created.

Resources