I tried to follow the 'Secure Upload' in carrier wave which is a bit confusing because I have customized the file path and all a bit. When I try to run the app, I get 'Cannot read file' error.
Here's the route :
match "/upload_files/:tenant_id/:model/:mount_as/:id/:basename.:extension" => "documents#download",via: [:get, :post]
class ImageUploader < CarrierWave::Uploader::Base
def store_dir
"upload_files/#{model.tenant_id}/#model.class.to_s.underscore}/#mounted_as}/#{model.id}"
end
end
carrierwave.rb initializer :
CarrierWave.configure do |config|
config.permissions = 0600
config.directory_permissions = 0700
config.root = Rails.root
end
documents controller:`
def download
path = request.fullpath
send_file path
end
got the error
ActionController::MissingFile in DocumentsController#download
Cannot read file /upload_files/1/hoshin_attachment/image/3/support3_HoshinUserStatusReports_08_14_2015.pdf
Please help me to find the solution
CarrierWave seems to compute paths using a root variable and the uploader store_dir.
I wanted to store my files in a private folder under Rails.root.
To setup the root:
# config/initializers/carrierwave.rb
# Uploader settings
CarrierWave.root = Rails.root.to_s
CarrierWave::Uploader::Base.root = Rails.root.to_s
To setup the store_dir:
# In your uploader class definition
def store_dir
"private/your_app/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
Since files are not longer in the public folder, they require actions in controller to show and download. Lets assume the column for the uploaded file is named uploaded_file:
# In some controller
# you have to ensure #document initialize with a before_action filter
def show_file
send_file #document.unsigned_file.path,
filename: #document.uploaded_file_identifier,
disposition: :inline,
type: #document.uploaded_file.content_type
end
def download_file
send_file #document.unsigned_file.path,
filename: #document.uploaded_file_identifier,
disposition: :attachment,
type: #document.uploaded_file.content_type
end
Related
I am using carrierwave fog-google configuration for file download and upload to GCS bucket. However, my concern is I wanted to have a signed URL returned from GCS response with some expiration time.
Is there any configuration I need to set which would help me in receiving a response from GCS which will have signed URL and will expire let say after an hour?
class TestUploader < CarrierWave::Uploader::Base
storage :fog
def fog_credentials
{
:provider => 'google',
:google_project =>'my project',
:google_json_key_location =>'myCredentialFile.json'
}
end
def fog_provider
'fog/google'
end
def fog_directory
'{#bucket-name}'
end
def store_dir
when :File
"#{file.getpath}/file"
when :audio
"#{file.getpath}/audio"
else
p " Invalid file "
end
end
end
I have 2 Carrierwave uploaders - ItemUploader and ImageUploader, and am using fog.
I can upload files to S3 just fine, but doing a destroy doesn't remove them from S3.
This is my destroy action:
def destroy
#item = Item.find(params[:id])
#item.destroy
respond_to do |format|
format.html { redirect_to items_url }
format.json { head :no_content }
end
end
When I do item.destroy, it deletes the record from my db but it doesn't remove the file from S3 and it doesn't remove the folders.
This is a brand new S3 bucket, with vanilla settings. Also a brand new Carrierwave install.
FYI: I have tried adding #item.remove_item! and #item.remove_image! to the destroy action of the controller but that hasn't done the trick either.
Edit 1
So it seems that what happens is that it deletes 1 of the attachments.
The model has this:
class Item < ActiveRecord::Base
# image :string(255)
# link :string(255)
mount_uploader :link, ItemUploader
mount_uploader :image, ImageUploader
end
So, when I delete an object in my console, it removes the object associated with ItemUploader and not the image related via the ImageUploader.
Why would it delete 1 and not the other?
It seems there is something wrong with my console - because once I delete the object via the web UI it deletes all the related objects in S3.
But if I do it via the console, it doesn't work.
I will be opening another SO question for that particular issue.
It deletes but take little time specially if you are referring file(image) from cdn.
Uses aws sdk - https://github.com/amazonwebservices/aws-sdk-for-ruby
You can build a facade to manage your objects on 3, for example:
require 'aws-sdk'
class Facades::AmazonFacade
attr_reader :s3
#
# Connection to Amazon S3
#
def initialize
#s3 = AWS::S3.new(
:access_key_id => config['access_key_id'],
:secret_access_key => config['secret_access_key']
)
#bucket = #s3.buckets[self.config['bucket']]
end
def config
##config ||= YAML::load(File.open("#{Rails.root}/config/amazon_s3.yml" ))[Rails.env]
end
####
def policy(bucket, options = {})
# Base 64 policy
end
def signature(bucket, options = {})
# Base64 signature
end
#
# Find object and get public urls
#
def url_link(obj, expires)
#bucket.objects[obj].url_for(:read, :secure => true, :expires => 10*60).to_s
end
def object_exists_on_amazon?(obj)
#bucket.objects[obj].exists?
end
def object_size(obj)
unless Rails.env.test?
#bucket.objects[obj].content_length
end
end
def object_upload_date(obj)
#bucket.objects[obj].last_modified
end
#
# create, delete objects
#
def store_object_on_amazon(obj, file, access)
#bucket.objects[obj].write(file, :acl => access)
end
def delete_object_on_amazon(obj)
#bucket.objects[obj].delete(:force => true)
end
end
I've read over the documentation on generators, but can't find any information about creating views. Reading through the code in the Rails 3 Generators gem, I found that you can override the default templates by placing new ones in lib/generators/erb/scaffold/templates/. You can also specify which views you want to create in the scaffold_generator.rb file with a snippet like:
def available_views
['index', 'edit', 'show', 'new', '_form']
end
So my question is, what if I wanted to create both an index.html.erb file and an index.js.erb file?
Apparently, the actual creation of the views is done by a function called copy_view_files. You can specify what type of view you want within that function. After doing so, my scaffold_generator.rb looks like this:
require 'rails/generators/erb/scaffold/scaffold_generator'
module Erb
module Generators
class ScaffoldGenerator < Erb::Generators::ScaffoldGenerator
source_root File.expand_path(File.join('..', 'templates'), __FILE__)
def copy_view_files
primary_views.each do |view|
filename = filename_with_extensions view
template "#{view}.html.erb", File.join('app', 'views', controller_file_path, filename)
template "#{view}.js.erb", File.join('app', 'views', controller_file_path, filename)
end
end
hook_for :form_builder, :as => :scaffold
protected
def primary_views
['index', 'edit', 'show', 'new']
end
def handler
:erb
end
end
end
end
Note that within the copy_view_files function, there are two calls to template. The first creates a set of html views, while the second creates the js views I wanted.
Similar answer I posted to the question https://stackoverflow.com/a/62441675/385730.
You can override the scaffold generator lib/rails/generators/erb/scaffold/scaffold_generator.rb file.
Step 1:
Copy latest scaffold_generator.rb file.
mkdir -p lib/rails/generators/erb/scaffold && cp $(bundle show railties)/lib/rails/generators/erb/scaffold/scaffold_generator.rb lib/rails/generators/erb/scaffold/
Step 2:
Add custom code to generate .js.erb files you want.
# frozen_string_literal: true
require "rails/generators/erb"
require "rails/generators/resource_helpers"
module Erb # :nodoc:
module Generators # :nodoc:
class ScaffoldGenerator < Base # :nodoc:
include Rails::Generators::ResourceHelpers
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_root_folder
empty_directory File.join("app/views", controller_file_path)
end
def copy_view_files
available_views.each do |view|
formats.each do |format|
filename = filename_with_extensions(view, format)
template filename, File.join("app/views", controller_file_path, filename)
end
end
javascript_views.each do |view|
path = File.join('app', 'views', controller_file_path, "#{view}.js.erb")
File.open(path, "w")
end
end
private
def available_views
%w(index edit show new _form)
end
def javascript_views
%w(index show create update)
end
end
end
end
Now when you run your scaffold generator you'll see the new .js.erb files that are created.
been trying to search the reason for this error for a long time and can't seem to find any...
So I have a rails app, and I utilize carrierwave for pictures uploading. I also want to utilize Amazon S3 for file upload storage in my app.
Initially as I am developing the app I allowed file uploads to be on the on :file, i.e.
image_uploader.rb
# Choose what kind of storage to use for this uploader:
storage :file
# storage :fog
Now upon finishing up development and placing it live (I use heroku), I decided to change the carrierwave storage to S3 to test it locally.
image_uploader.rb
# Choose what kind of storage to use for this uploader:
# storage :file
storage :fog
However, now when I try to upload a picture (be it for user avatar, etc) I get this error:
Excon::Errors::Forbidden in UsersController#update
Expected(200) <=> Actual(403 Forbidden)
request => {:connect_timeout=>60, :headers=>{"Content-Length"=>74577, "x-amz- acl"=>"private", "Content-Type"=>"image/png", "Date"=>"Sun, 26 Feb 2012 10:00:43 +0000", "Authorization"=>"AWS AKIAJOCDPFOU7UTT4HOQ:8ZnOy7X71nQAM87yraSI24Y5bSw=", "Host"=>"s3.amazonaws.com:443"}, :instrumentor_name=>"excon", :mock=>false, :read_timeout=>60, :retry_limit=>4, :ssl_verify_peer=>true, :write_timeout=>60, :host=>"s3.amazonaws.com", :path=>"/uploads//uploads%2Fuser%2Favatar%2F1%2Fjeffportraitmedium.png", :port=>"443", :query=>nil, :scheme=>"https", :body=>"\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\xC2\x00\x00\x00\xC3\b\x06\x00\x00\x00\xD0\xBD\xCE\x94\x00\x00\nCiCCPICC Profile\x00\x00x\x01\x9D\x96wTSY\x13\xC0\xEF{/\xBD\xD0\x12B\x91\x12z\rMJ\x00\x91\x12z\x91^E%$\
...
# The code you see above to the far right repeats itself a LOT
...
1#\x85\xB5\t\xFC_y~\xA6=:\xB2\xD0^\xBB~i\xBB\x82\x8F\x9B\xAF\xE7\x04m\xB2i\xFF\x17O\x94S\xF7l\x87\xA8&\x00\x00\x00\x00IEND\xAEB`\x82", :expects=>200, :idempotent=>true, :method=>"PUT"}
response => #<Excon::Response:0x007fc88ca9f3d8 #body="<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>8EFA56C0DDDC8878</RequestId><HostId>1OxWXppSSUq1MFjQwvnFptuCM3gKOuKdlQQyVSEgvzzv4Aj+r2hSFM2UUw2NYyrR</HostId></Error>", #headers={"x-amz-request-id"=>"8EFA56C0DDDC8878", "x-amz-id-2"=>"1OxWXppSSUq1MFjQwvnFptuCM3gKOuKdlQQyVSEgvzzv4Aj+r2hSFM2UUw2NYyrR", "Content-Type"=>"application/xml", "Transfer-Encoding"=>"chunked", "Date"=>"Sun, 26 Feb 2012 10:00:47 GMT", "Connection"=>"close", "Server"=>"AmazonS3"}, #status=403>
And then it says this as well for my application trace:
app/controllers/users_controller.rb:39:in `update'
And my REQUEST parameters:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"DvADD1vYpCLcghq+EIOwVSjsfmAWCHhtA3VI5VGD/q8=",
"user"=>{"avatar"=>#<ActionDispatch::Http::UploadedFile:0x007fc88cde76f8
#original_filename="JeffPortraitMedium.png",
#content_type="image/png",
#headers="Content-Disposition: form-data; name=\"user[avatar]\";
filename=\"JeffPortraitMedium.png\"\r\nContent-Type: image/png\r\n",
#tempfile=#<File:/var/folders/vg/98nv58ss4v7gcbf8px_8dyqc0000gq/T/RackMultipart20120226- 19096-1ppu2sr>>,
"remote_avatar_url"=>"",
"name"=>"Jeff Lam ",
"email"=>"email#gmail.com",
"user_bio"=>"Tester Hello",
"shop"=>"1"},
"commit"=>"Update Changes",
"id"=>"1"}
Here's my users_controller.rb partial code:
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
redirect_back_or root_path
flash[:success] = "Your have updated your settings successfully."
else
flash.now[:error] = "Sorry! We are unable to update your settings. Please check your fields and try again."
render 'edit'
end
end
My image_uploader.rb code
# encoding: utf-8
class ImageUploader < CarrierWave::Uploader::Base
# Include RMagick or MiniMagick support:
# include CarrierWave::RMagick
include CarrierWave::MiniMagick
# Choose what kind of storage to use for this uploader:
# storage :file
storage :fog
# 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
# Provide a default URL as a default if there hasn't been a file uploaded:
# def default_url
# "/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 resize_to_fill: [360, 250]
end
version :cover_photo_thumb do
process resize_to_fill: [1170, 400]
end
version :event do
process resize_to_fill: [550, 382]
end
version :product do
process resize_to_fit: [226, 316]
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
# fix for Heroku, unfortunately, it disables caching,
# see: https://github.com/jnicklas/carrierwave/wiki/How-to%3A-Make-Carrierwave-work-on-Heroku
def cache_dir
"#{Rails.root}/tmp/uploads"
end
end
Finally, my fog.rb file in the config/initializers
CarrierWave.configure do |config|
config.fog_credentials = {
:provider => 'AWS', # required
:aws_access_key_id => 'ACCESS_KEY', # required
:aws_secret_access_key => 'SECRET_ACCESS_KEY/ZN5SkOUtOEHd61/Cglq9', # required
:region => 'Singapore' # optional, defaults to 'us-east-1'
}
config.fog_directory = 'ruuva/' # required
config.fog_public = false # optional, defaults to true
end
I'm actually quite confused on some of the things in my fog.rb. Firstly, should I change my region to Singapore if I created a bucket called "ruuva", with region "Singapore" on my amazon s3 account?
Thank you to anyone that can help in advance!
First make sure you use the right credentials by not setting custom region and custom directory (create a fake bucket for free in the default region)
Then I think you are not using the right name for the region. Try setting your region like this:
:region => 'ap-southeast-1'
We were facing the same problem and fixed that changing the user's permission associated to your access key, changing it to "Power User". Check if you need your user to be power user before put it into productions.
Is there a (simple) way to configure the default generator to also generate a js.erb file for each action in addition to html.erb files?
You can override the scaffold generator lib/rails/generators/erb/scaffold/scaffold_generator.rb file.
Step 1:
Copy latest scaffold_generator.rb file.
mkdir -p lib/rails/generators/erb/scaffold && cp $(bundle show railties)/lib/rails/generators/erb/scaffold/scaffold_generator.rb lib/rails/generators/erb/scaffold/
Step 2:
Add custom code to generate .js.erb files you want.
# frozen_string_literal: true
require "rails/generators/erb"
require "rails/generators/resource_helpers"
module Erb # :nodoc:
module Generators # :nodoc:
class ScaffoldGenerator < Base # :nodoc:
include Rails::Generators::ResourceHelpers
argument :attributes, type: :array, default: [], banner: "field:type field:type"
def create_root_folder
empty_directory File.join("app/views", controller_file_path)
end
def copy_view_files
available_views.each do |view|
formats.each do |format|
filename = filename_with_extensions(view, format)
template filename, File.join("app/views", controller_file_path, filename)
end
end
javascript_views.each do |view|
path = File.join('app', 'views', controller_file_path, "#{view}.js.erb")
File.open(path, "w")
end
end
private
def available_views
%w(index edit show new _form)
end
def javascript_views
%w(index show create update)
end
end
end
end
Now when you run your scaffold generator you'll see the new .js.erb files that are created.
The answer from #dale-zak it created an empty js file without the contents from my lib/templates/erb/scaffold/index.js.erb.tt
That code with this loop works for me:
javascript_views.each do |view|
filename = filename_with_extensions(view, :js)
template filename, File.join("app/views", controller_file_path, filename)
end