Code Monkey home page Code Monkey logo

carrierwave_direct's Introduction

CarrierWaveDirect

Build Status

CarrierWave is a great way to upload files from Ruby applications, but since processing and saving is done in-process, it doesn't scale well. A better way is to upload your files directly then handle the processing and saving in a background process.

CarrierWaveDirect works on top of CarrierWave and provides a simple way to achieve this.

Example Application

For a concrete example on how to use CarrierWaveDirect in a Rails application check out the Example Application.

Compatibility

Right now, CarrierWaveDirect works with Amazon S3. Adding support for Google Storage for Developers should be fairly straight forward since the direct upload form is essentially the same. Please see the contributing section if you would like support for Google Storage for Developers or any other service that provides direct upload capabilities.

Please be aware that this gem (and S3 in general) only support single file uploads. If you want to upload multiple files simultaneously you'll have to use a javascript or flash uploader.

Installation

Install the latest release:

gem install carrierwave_direct

In Rails, add it to your Gemfile:

gem 'carrierwave_direct'

Note that CarrierWaveDirect is not compatible with Rails 2.

Getting Started

Please read the CarrierWave readme first

CarrierWaveDirect works with fog so make sure you have CarrierWave set up and initialized with your fog credentials, for example:

CarrierWave.configure do |config|
  config.fog_credentials = {
    :provider               => 'AWS',       # required
    :aws_access_key_id      => 'xxx',       # required
    :aws_secret_access_key  => 'yyy',       # required
    :region                 => 'eu-west-1'  # optional, defaults to 'us-east-1'
  }
  config.fog_directory  = 'name_of_your_aws_bucket' # required
  # see https://github.com/jnicklas/carrierwave#using-amazon-s3
  # for more optional configuration
end

If you haven't already done so generate an uploader

rails generate uploader Avatar

this should give you a file in:

app/uploaders/avatar_uploader.rb

Check out this file for some hints on how you can customize your uploader. It should look something like this:

class AvatarUploader < CarrierWave::Uploader::Base
  storage :file
end

Remove the line storage :file and replace it with include CarrierWaveDirect::Uploader so it should look something like:

class AvatarUploader < CarrierWave::Uploader::Base
  include CarrierWaveDirect::Uploader
end

This adds the extra functionality for direct uploading.

Optional: Remove the store_dir method in order to default CarrierWaveDirect to its own storage directory.

/uploads/<unique_guid>/foo.png

If you're not using Rails you can generate a direct upload form to S3 similar to this example by making use of the CarrierWaveDirect helper methods.

Sinatra

Here is an example using Sinatra and Haml

# uploader_test.rb

CarrierWave.configure do |config|
  config.fog_credentials = {
    :provider               => 'AWS',
    :aws_access_key_id      => ENV['AWS_ACCESS_KEY_ID'],
    :aws_secret_access_key  => ENV['AWS_SECRET_ACCESS_KEY']
  }
  config.fog_directory  = ENV['AWS_FOG_DIRECTORY'] # bucket name
end

class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWaveDirect::Uploader
end

class UploaderTest < Sinatra::Base
  get "/" do
    @uploader = ImageUploader.new
    @uploader.success_action_redirect = request.url
    haml :index
  end
end
# index.haml
# Now using AWS POST authentication V4
# See http://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html for more information

%form{:action => @uploader.direct_fog_url, :method => "post", :enctype => "multipart/form-data"}
  - @uploader.direct_fog_hash.each do |key, value|
    - if key != :uri
      %input{:type => "hidden", :name => key, :value => value}
  %input{:type => "hidden", :name => "success_action_redirect", :value => @uploader.success_action_redirect}
  %input{:name => "file", :type => "file"}
  %input{:type => "submit", :value => "Upload to S3"}

Rails

If you are using Rails and you've mounted your uploader like this:

class User < ActiveRecord::Base
  mount_uploader :avatar, AvatarUploader
end

things just got a whole lot easier. You can generate a direct upload form like this:

class AvatarController < ApplicationController
  def new
    @uploader = User.new.avatar
    @uploader.success_action_redirect = new_user_url
  end
end
<%= direct_upload_form_for @uploader do |f| %>
  <%= f.file_field :avatar %>
  <%= f.submit %>
<% end %>

After uploading to S3, You'll need to update the uploader object with the returned key in the controller action that corresponds to new_user_url:

@uploader.update_attribute :avatar_key, params[:key]

You can also pass html options like this:

<%= direct_upload_form_for @uploader, :html => { :target => "_blank_iframe" } do |f| %>
  <%= f.file_field :avatar %>
  <%= f.submit %>
<% end %>

Note if User is not an ActiveRecord object e.g.

class User
  extend CarrierWave::Mount
  extend CarrierWaveDirect::Mount

  mount_uploader :avatar, AvatarUploader
end

you can still use the form helper by including the ActiveModel modules your uploader:

class AvatarUploader < CarrierWave::Uploader::Base
  include CarrierWaveDirect::Uploader

  include ActiveModel::Conversion
  extend ActiveModel::Naming
end

Note if you're using Rails 3.0.x you'll also need to disable forgery protection

# config/application.rb
config.action_controller.allow_forgery_protection = false

Once you've uploaded your file directly to the cloud you'll probably need a way to reference it with an ORM and process it.

Content-Type / Mime

The default amazon content-type is "binary/octet-stream" and for many cases this will work just fine. But if you are trying to stream video or audio you will need to set the mime type manually as Amazon will not calculate it for you. All mime types are supported: http://en.wikipedia.org/wiki/Internet_media_type.

First, tell CarrierWaveDirect what your default content type should be:

CarrierWave.configure do |config|
  # ... fog configuration and other options ...
  config.will_include_content_type = true

  config.default_content_type = 'video/mpeg'
  config.allowed_content_types = %w(video/mpeg video/mp4 video/ogg)
end

or

class VideoUploader < CarrierWave::Uploader::Base
  include CarrierWaveDirect::Uploader

  def will_include_content_type
    true
  end

  default_content_type  'video/mpeg'
  allowed_content_types %w(video/mpeg video/mp4 video/ogg)
end

Note: If will_include_content_type is true and default_content_type is nil, the content type will default to 'binary/octet-stream'.

For the forms, no change is required to use the default_content_type.

<%= direct_upload_form_for @uploader do |f| %>
  <%= f.file_field :avatar %> # Content-Type will automatically be set to the default
  <%= f.submit %>
<% end %>

You could use a manual select as well.

<%= direct_upload_form_for @uploader do |f| %>
  <%= select_tag 'Content-Type', options_for_select([
    ['Video','video/mpeg'],
    ['Audio','audio/mpeg'],
    ['Image','image/jpeg']
  ], 'video/mpeg') %><br>
  <%= f.file_field :avatar, exclude_content_type: true %> # Must tell the file_field helper not to include content type
  <%= f.submit %>
<% end %>

Or you can use the helper which shows all possible content types as a select, with the default content type selected.

<%= direct_upload_form_for @uploader do |f| %>
  <%= f.content_type_label %><br>
  <%= f.content_type_select %><br><br>

  <%= f.file_field :avatar, exclude_content_type: true %><br> # Must tell the file_field helper not to include its own content type
  <%= f.submit %>
<% end %>

Processing and referencing files in a background process

Processing and saving file uploads are typically long running tasks and should be done in a background process. CarrierWaveDirect gives you a few methods to help you do this with your favorite background processor such as DelayedJob or Resque.

If your upload was successful then you will be redirected to the success_action_redirect url you specified in your uploader. S3 replies with a redirect like this: http://example.com?bucket=your_fog_directory&key=uploads%2Fguid%2Ffile.ext&etag=%22d41d8cd98f00b204e9800998ecf8427%22

The key is the most important piece of information as we can use it for validating the file extension, downloading the file from S3, processing it and re-uploading it.

If you're using ActiveRecord, CarrierWaveDirect will by default validate the file extension based off your extension_allowlist in your uploader. See the CarrierWave readme for more info. You can then use the helper filename_valid? to check if the filename is valid. e.g.

class UsersController < ApplicationController
  def new
    @user = User.new(params)
    unless @user.filename_valid?
      flash[:error] = @user.errors.full_messages.to_sentence
      redirect_to new_avatar_path
    end
  end
end

CarrierWaveDirect automatically gives you an accessible key attribute in your mounted model when using ActiveRecord. You can use this to put a hidden field for the key into your model's form.

<%= form_for @user do |f| %>
  <%= f.hidden_field :key %>
  <%= f.label :email %>
  <%= f.text_field :email %>
  <%= f.submit %>
<% end %>

then in your controller you can do something like this:

def create
  @user = User.new(params[:user])
  if @user.save_and_process_avatar
    flash[:notice] = "User being created"
    redirect_to :action => :index
  else
    render :new
  end
end

Background processing

Now that the basic building blocks are in place you can process and save your avatar in the background. This example uses Resque but the same logic could be applied to DelayedJob or any other background processor.

class User < ActiveRecord::Base
  def save_and_process_avatar(options = {})
    if options[:now]
      self.remote_avatar_url = avatar.url
      save
    else
      Resque.enqueue(AvatarProcessor, attributes)
    end
  end
end

class AvatarProcessor
  @queue = :avatar_processor_queue

  def self.perform(attributes)
    user = User.new(attributes)
    user.save_and_process_avatar(:now => true)
  end
end

The method self.remote_avatar_url= from CarrierWave downloads the avatar from S3 and processes it. save then re-uploads the processed avatar to S3

Uploading from a remote location

Your users may find it convenient to upload a file from a location on the Internet via a URL. CarrierWaveDirect gives you another accessor to achieve this.

<%= form_for @user do |f| %>
  <%= f.hidden_field :key %>
  <% unless @user.has_avatar_upload? %>
    <%= f.label :remote_avatar_net_url %>
    <%= f.text_field :remote_avatar_net_url %>
  <%= f.submit %>
<% end %>
class User < ActiveRecord::Base
  def save_and_process_avatar(options = {})
    if options[:now]
      self.remote_avatar_url = has_remote_avatar_net_url? ? remote_avatar_net_url : avatar.url
      save
    else
      Resque.enqueue(AvatarProcessor, attributes)
    end
  end
end

The methods has_avatar_upload?, remote_avatar_net_url and has_remote_avatar_net_url? are automatically added to your mounted model

Validations

Along with validating the extension of the filename, CarrierWaveDirect also gives you some other validations:

validates :avatar :is_uploaded => true

Validates that your mounted model has an avatar uploaded from file or specified by remote url. It does not check that an your mounted model actually has a valid avatar after the download has taken place. Turned off by default

validates :avatar, :is_attached => true

Validates that your mounted model has an avatar attached. This checks whether there is an actual avatar attached to the mounted model after downloading. Turned off by default

validates :avatar, :filename_uniqueness => true

Validates that the filename in the database is unique. Turned on by default

validates :avatar, :filename_format => true

Validates that the uploaded filename is valid. As well as validating the extension against the extension_allowlist it also validates that the upload_dir is correct. Turned on by default

validates :avatar, :remote_net_url_format => true

Validates that the remote net url is valid. As well as validating the extension against the extension_allowlist it also validates that url is valid and has only the schemes specified in the url_scheme_whitelist. Turned on by default

Configuration

As well as the built in validations CarrierWaveDirect provides, some validations, such as max file size and upload expiration can be performed on the S3 side.

CarrierWave.configure do |config|
  config.validate_is_attached = true             # defaults to false
  config.validate_is_uploaded = true             # defaults to false
  config.validate_unique_filename = false        # defaults to true
  config.validate_filename_format = false        # defaults to true
  config.validate_remote_net_url_format = false  # defaults to true

  config.min_file_size             = 5.kilobytes  # defaults to 1.byte
  config.max_file_size             = 10.megabytes # defaults to 5.megabytes
  config.upload_expiration         = 1.hour       # defaults to 10.hours
  config.will_include_content_type = 'video/mp4'  # defaults to false; if true, content-type will be set
                                                  # on s3, but you must include an input field named
                                                  # Content-Type on every direct upload form

  config.use_action_status = true                 # defaults to false; if true, you must set
                                                  # success_action_status in your uploader:
                                                  #   uploader.success_action_status = "201"
                                                  # and add use_action_status to the file field:
                                                  #   f.file_field :avatar, use_action_status: true'
end

Bucket CORS configuration and usage with CloudFront

If you are using a javascript uploader (e.g. Dropzone, jQuery Upload, Uploadify, etc.) you will need to add CORS configuration to your bucket, otherwise default bucket configuration will deny CORS requests. To do that open your bucket in Amazon console, click on its properties and add a CORS configuration similar to this

<CORSConfiguration>
    <CORSRule>
        <!--  Optionally change this with your domain, it should not be an issue since your bucket accepts only signed writes -->
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
        <AllowedHeader>Content-Type</AllowedHeader>
        <AllowedHeader>Origin</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

When you use this gem in conjunction with CloudFront you'll need an additional step otherwise it won't work as expected. This is strictly necessary if you configured CarrierWave asset_host to use a CloudFront in front of your bucket because your forms will be posted to that url.

To solve this issue you must enable POST requests in your CloudFront distribution settings. Here is a step by step walkthrough that explain this setup. It also explain CORS configuration.

Testing with CarrierWaveDirect

CarrierWaveDirect provides a couple of helpers to help with integration and unit testing. You don't want to contact the Internet during your tests as this is slow, expensive and unreliable. You should first put fog into mock mode by doing something like this.

Fog.mock!

def fog_directory
  ENV['AWS_FOG_DIRECTORY']
end

connection = ::Fog::Storage.new(
  :aws_access_key_id      => ENV['AWS_ACCESS_KEY_ID'],
  :aws_secret_access_key  => ENV['AWS_SECRET_ACCESS_KEY'],
  :provider               => 'AWS'
)

connection.directories.create(:key => fog_directory)

Using Capybara

If you're using Capybara with Cucumber or RSpec, CarrierWaveDirect gives you a few useful helpers. To get the Capybara helpers, include the module into your test file or helper

describe AvatarUploadSpec
  include CarrierWaveDirect::Test::CapybaraHelpers
end

To attach a file to the direct upload form you can use

attach_file_for_direct_upload('path/to/file.ext')

To simulate a successful upload and redirect to S3 you can use

upload_directly(AvatarUploader.new, "Upload to S3")

This will click the Upload to S3 button on the form and redirect you to the success_action_redirect url (in the form) with a sample response from S3

To simulate an unsuccessful upload you can pass :success => false and you'll remain on the upload page e.g.

upload_directly(AvatarUploader.new, "Upload to S3", :success => false)

You can also use find_key and find_upload_path to get the key and upload path from the form

Unit tests

If your mounted model validates a file is uploaded you might want to make use of the sample_key method

include CarrierWaveDirect::Test::Helpers

Factory.define :user |f|
  f.email "[email protected]"
  f.key { sample_key(AvatarUploader.new) }
end

This will return a valid key based off your upload_dir and your extension_allowlist

Faking a background download

If you wanted to fake a download in the background you could do something like this

uploader = AvatarUploader.new

upload_path = find_upload_path
redirect_key = sample_key(:base => find_key, :filename => File.basename(upload_path))

uploader.key = redirect_key
download_url = uploader.url

# Register the download url and return the uploaded file in the body
FakeWeb.register_uri(:get, download_url, :body => File.open(upload_path))

i18n

The Active Record validations use the Rails i18n framework. Add these keys to your translations file:

en:
  errors:
    messages:
      carrierwave_direct_filename_taken: filename was already taken
      carrierwave_direct_upload_missing: upload is missing
      carrierwave_direct_attachment_missing: attachment is missing
      carrierwave_direct_filename_invalid: "is invalid. "
      carrierwave_direct_allowed_extensions: Allowed file types are %{extensions}
      carrierwave_direct_allowed_schemes: Allowed schemes are %{schemes}

Caveats

If you're Rails app was newly generated after version 3.2.3 and your testing this in development you may run into an issue where an ActiveModel::MassAssignmentSecurity::Error is raised when being redirected from S3. You can fix this by setting config.active_record.mass_assignment_sanitizer = :logger in your config/environments/development.rb file.

Contributing to CarrierWaveDirect

Pull requests are very welcome. Before submitting a pull request, please make sure that your changes are well tested. Pull requests without tests will not be accepted.

gem install bundler
bundle install

You should now be able to run the tests

bundle exec rake

Using the Sample Application

After you have fixed a bug or added a feature please also use the CarrierWaveDirect Sample Application to ensure that the gem still works correctly.

Credits

Thank you to everybody who has contributed to CarrierWaveDirect. A special thanks to nddeluca (Nicholas DeLuca) who does a great job maintaining this gem. If you'd like to help him out see #83

Contributors

carrierwave_direct's People

Contributors

ariejan avatar artur79 avatar cblunt avatar colinyoung avatar dunghuynh avatar dwilkie avatar fabn avatar filiptepper avatar fran-worley avatar greysteil avatar hanachin avatar javierchik avatar jkamenik avatar kreintjes avatar lawso017 avatar lsimoneau avatar mois3x avatar nddeluca avatar p8 avatar parndt avatar rauhryan avatar rebyn avatar rhalff avatar samuelreh avatar sevos avatar stephanschubert avatar stuartmenefy avatar travisp avatar vinniefranco avatar vlado avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

carrierwave_direct's Issues

Update Readme for HTML form

Currently the HTML form is confusing and incomplete:

<form action="https://s3-bucket.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
  <input type="hidden" name="key" value="uploads/${filename}">
  <input type="hidden" name="AWSAccessKeyId" value="YOUR_AWS_ACCESS_KEY">
  <input type="hidden" name="acl" value="private">
  <input type="hidden" name="success_action_redirect" value="http://localhost/">
  <input type="hidden" name="policy" value="YOUR_POLICY_DOCUMENT_BASE64_ENCODED">
  <input type="hidden" name="signature" value="YOUR_CALCULATED_SIGNATURE">
  <input name="file" type="file">
  <input type="submit" value="Upload to S3">
</form>

None of the above should be hard-coded. All of them should get from the values of uploader, including the action link. There is also a missing hidden input field for utf8 too.

Invalid according to Policy: Policy Condition failed

AccessDenied Invalid according to Policy: Policy Condition failed: ["starts-with", "$Content-Type", ""]

Following the installation instructions I get the above error from s3.
Reverting to
gem 'carrierwave_direct', '0.0.8'
corrects the issue.

I tried it with and without:

include Sprockets::Helpers::RailsHelper
include Sprockets::Helpers::IsolatedHelper
include CarrierWave::MimeTypes
process :set_content_type

Upload within a model form

I'm not sure if this is the place for questions, so please delete it if innapropriate. I just couldn't find a forum or another place that seemed better suited.

I'm uploading a user avatar with carrierwave, which works inside a regular form_for. So I get the avatar along with all the rest of the user info.

However, with carrierwave_direct, something like this would not work, since I would need to nest the direct_upload_form_for inside a form_for. Something like this:

<%= form_for @user, :html => { :multipart => true } do |f| %>
  <%= f.error_messages %>
  <%= f.label "What's your name?" %>
  <%= f.text_field :name %>
  <%= f.label "What's your birthday?" %>
  <%= f.date_select :birthday %>
  <%= direct_upload_form_for @uploader do %>
    <%= f.label "Upload your avatar" %>
    <%= f.file_field :avatar %>
    <%= f.submit %>
  <% end %>
  <%= f.submit =>
<% end %>

Anyway to get around this? I would really love to avoid a flash uploader.

Thanks for the great gem! Hope I'm able to get it working on my scenario.

Destroying object does not delete from S3?

In rails when I call destroy in my model that has a uploader mounted it does not remove the attachment from S3. Is a bug with my code or the plugin avoids it?

Thanks

stack level too deep

When i include the carrierwave_direct gem I get the following error:

    stack level too deep

Full Trace

    carrierwave_direct (0.0.2) lib/carrierwave_direct/mount.rb:8

mount.rb

      uploader.class_eval <<-RUBY, __FILE__, __LINE__+1
        def #{column}; self; end
      RUBY

rails 3.1.0, ruby 1.9.2, carrierwave 0.5.8, carrierwave_direct 0.0.2

I'm using a standard uploader (as shown in the readme).

What am I doing wrong? Thanks in advance!

Find a new maintainer

I no longer use CarrierWaveDirect in any projects, so I'm no longer a good person to help with maintenance. Is anyone interested?

@colinyoung are you interested?

Processing a file

After downloading a file:

def self.perform(user_id, avatar_key)
user = User.find(user_id)
user.key = avatar_key
user.remote_avatar_url = user.avatar.direct_fog_url(:with_path => true)
user.save!
end

How do you get the file that's saved in tmp? How do I reupload it back within a queue?

Uploads are left hanging

Hi, i am having a small problem uploading files to s3.

I have followed the instructions for installing and setting up carrierwave direct, however when i try to upload to s3 the upload begins and then freezes the upload at some point. Any thoughts?

Also sometimes i get this message from s3:

<Error>
<Code>InvalidPolicyDocument</Code>
<Message>
      Invalid Policy: Invalid 'expiration' value: '2011-09-13T07:52:58+02:00'
</Message>
<RequestId>"some id"</RequestId>
<HostId>
      "some host id"
</HostId>
</Error>

I have created an initializes file called carrierwave.rb with all my credentials from amazon s3. I have also loaded all the required gems(carrierwave_diirect, fog, carrierwave, nokogiri)

Checkout my code here

Been waiting a long time for a gem like this, thumbs up
Thank for for the help

Issue when recreate versions

I have a version namespace that should process conditionally some others versions.

class ArticleImage < ActiveRecord::Base
  belongs_to :asset
  belongs_to :article

  before_save do
    AssetVersionsWorker.perform_async(asset.id) if asset_id_changed? && !asset.illustrable
  end

  class AssetVersionsWorker
    include Sidekiq::Worker

    def perform(asset_id)
      asset = Asset.find(asset_id)
      asset.update_attribute(:illustrable, true)
      # asset.image.cache_stored_file!
      # asset.image.retrieve_from_cache!(asset.image.cache_name) 
      asset.image.recreate_versions!
    end
  end

end
# Encoding: utf-8
class AssetUploader < CarrierWave::Uploader::Base
  include CarrierWaveDirect::Uploader
  include CarrierWave::MiniMagick

  ## Callbacks
  before :cache, :capture_size_before_cache

  ## Versions
  version :thumbnail do
    process resize_to_fill: [70, 50]
  end

  version :content_thumb do
    process :resize_to_fill => [140, 80]
  end

  version :actu, if: :is_illustrable? do
    version :gabarit do
      process :resize_to_limit => [650, 650]
    end

    version :big_banderolle do
      process :crop
      process :resize_to_fill => [610, 200]
    end

    version :banderolle do
      process :crop
      process :resize_to_fill => [300, 100]
    end

    version :big_slider do
      process :resize_to_fill => [651, 334]
    end
  end

  ## Options
  def url(options={})
    super.split("?v=")[0]+"?v=#{model.updated_at.to_time.to_i}" rescue super
  end

  def store_dir
    "uploads/assets"
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

private

  def capture_size_before_cache(new_file) 
    if model.width.nil?
      model.width, model.height = `identify -format "%wx %h" #{new_file.path}`.split(/x/).map { |dim| dim.to_i }
    end
  end

  def is_illustrable?(file)
    return model.illustrable unless model.illustrable.nil?
  end

  def crop
    if model.crop_x.present?
      resize_to_limit(650, 650)
      manipulate! do |img|
        x = model.crop_x
        y = model.crop_y
        w = model.crop_w
        h = model.crop_h

        params = "#{w}x#{h}+#{x}+#{y}"
        img.crop(params)
        img.strip

        img = yield(img) if block_given?
        img
      end
    end
  end
end

https://gist.github.com/6bf132c5647531517982 <- sidekiq log

or

1.9.3-p194 :005 > a = Asset.first
  Asset Load (0.4ms)  SELECT "assets".* FROM "assets" LIMIT 1
 => #<Asset id: 7, user_id: nil, name: "Michael Jordan1", image: "1bac3350-f35a-012f-e4af-001d7d080a4f/michael-jordan...", image_processed: true, crop_x: nil, crop_y: nil, crop_w: nil, crop_h: nil, width: 666, height: 481, illustrable: true, created_at: "2012-10-08 09:40:28", updated_at: "2012-10-08 09:40:40"> 
1.9.3-p194 :006 > a.image.recreate_versions!
TypeError: can't convert nil into String
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave_direct-0.0.7/lib/carrierwave_direct/uploader.rb:133:in `extname'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave_direct-0.0.7/lib/carrierwave_direct/uploader.rb:133:in `full_filename'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave-0.6.2/lib/carrierwave/uploader/store.rb:43:in `store_path'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave-0.6.2/lib/carrierwave/storage/fog.rb:83:in `store!'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave-0.6.2/lib/carrierwave/uploader/store.rb:59:in `block in store!'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave-0.6.2/lib/carrierwave/uploader/callbacks.rb:17:in `with_callbacks'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave-0.6.2/lib/carrierwave/uploader/store.rb:58:in `store!'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/carrierwave-0.6.2/lib/carrierwave/uploader/versions.rb:176:in `recreate_versions!'
    from (irb):6
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.8/lib/rails/commands/console.rb:47:in `start'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.8/lib/rails/commands/console.rb:8:in `start'
    from /home/foohey/.rvm/gems/ruby-1.9.3-p194/gems/railties-3.2.8/lib/rails/commands.rb:41:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

that happen only when recreating versions

URI::InvalidURIError (bad URI(is not URI?)) occurred with a URL which contains special characters

I'm using carrierwave_direct 0.0.8 with rails 3.2.9 referred to carrierwave_direct_example.
Seems a file uploaded that the name contains special characters (i.e. 'รค', multi-byte characters and so on) raises this error.
The file can be saved, but when being updated, the error occurred from line 45 on carrierwave_direct/lib/carrierwave_direct/uploader.rb (the 'key' method).
So I've fixed this line:

# in the 'key' method
self.key = URI.parse(url).path # explicitly set key

to:

self.key = URI.parse(URI.encode(url)).path # explicitly set key

URI.encode did the trick for me.
Is there a proper way to fix this?

Thanks!

Update README for upload_directly helper for Capybara tests

I found that the upload_directly helper for Capybary tests should be as following:

upload_directly(AvatarUploader.new, "Upload to S3")

not

upload_directly(AvatarUploader, "Upload to S3")

Should it be correct?

Here is the my test:

visit edit_project_path(@project)
attach_file_for_direct_upload(Rails.root.join('spec', 'support', 'videos', 'media.mp4'))
upload_directly(VideoUploader.new, "Upload Video")
#... assertion

Only restricted to public resources?

The direct_fog_url method in CarrierWaveDirect::Uploader seems to only work for public S3 files. Can this work with files which are uploaded privately?

Stopped working with carrierwave 0.6.0

0.6.0 had some breaking changes. For example, the following error occurs:

undefined local variable or method `s3_access_policy' for :ImageUploader

This is because there is no longer an s3_access_policy. Not sure if this is the only incompatibility.

Migrating from carrierwave

I have a form, uploader, DataMapper model, and S3 currently working together properly (if a bit slow), and I have the advantage of not needing to do any processing whatsoever. I simply want to upload files to S3, and I am done. My first question is simply if carrierwave_direct offers any benefit in this scenario.

If the answer is yes, my second question involves the minimum effort necessary to migrate from carrierwave to carrierwave_direct. For example, is it a requirement to create a direct_upload_form_for? I only ask because I have a complicated form using jQuery-File-Upload and potentially multiple files coming from multiple file input elements, and this form works off one of my domain models and not a particular instance of an uploader.

Any insight is appreciated.

Thanks.

Assumes one Uploader per model. Is this correct?

From the Carrierwave::ActiveRecord module in the mount_uploader methods there is:

 self.instance_eval <<-RUBY, __FILE__, __LINE__+1
    attr_accessor   :skip_is_attached_validations
    attr_accessible :key, :remote_#{column}_net_url
  RUBY

If I am correct (I am using mongoid so I have not verified it) the attr_accessible :key would not work for two uploaders in one model?

If so, are there any other areas where this holds true?

How do you validate presence?

After uploading to Amazon, when the key comes back, then I'm supposed to save the key. The key magically populates the avatar field in Users. So, if I validate avatar with presence, it will return back false because it hasn't been populated yet during the validation of the active record cycle, but the whole idea of trying to validate presence is so that the form doesn't send a request to Amazon. What's the proper way to validate presence then?

Multiple uploads

This is not an issue, but more of a question, but i don't see any other method of asking so...

Is there a way to support multiple file uploads with this library?

Why the mounter filename method always returns nl?

I have a model called Document. It has the mounter attribute :attachment

Every time I pick Document.last.attachment.filename it returns nil.

I see in the code the filename method:

    def filename
      unless has_key?
        # Use the attached models remote url to generate a new key otherwise return nil
        remote_url = model.send("remote_#{mounted_as}_url")
        remote_url ? key_from_file(CarrierWave::SanitizedFile.new(remote_url).filename) : return
      end

      key_path = key.split("/")
      filename_parts = []
      filename_parts.unshift(key_path.pop)
      unique_key = key_path.pop
      filename_parts.unshift(unique_key) if unique_key
      filename_parts.join("/")
    end

If I call Document.last.attachment.has_key? it returns false...
But
Document.last.attachment.key gives me:
"assets/7e08d8f0-2783-0130-fea7-60fb42ee9ec8/${filename}"

How does one duplicate a file?

I've got an Avatar class with an AvatarUploader. The carrierwave attribute is Avatar.image. The files are in S3, and have 3 thumbnail versions.

I need to be able to "clone" avatars; this includes creating (or scheduling the creation of) a copy of the files in fog.

I have tried several variations of the following code, but none worked. Depending on the exact attributes I set, I get validation errors ("filename already taken" or "filename extension is not included on the accepted lists) or nil access errors.

class Avatar < ActiveRecord::Base

def duplicate(new_owner)
  new_avatar = new_owner.build_avatar
  new_avatar.remote_image_url = self.image_url
  # also tried:
  # new_avatar.remote_image_net_url = self.image_url
  new_avatar.save!
end

Any ideas on how to crack this one? I'm starting to guess I will need to make a post to S3 at some point, but I'm unsure of where to do it.

How to use in fields_for

Hi - I would like to integrate this gem - however curious if someone can give me an example of using it in a fields_for type implementation.

I already have carrierwave working well - for instance, updating a user profile and their logo all in the same form. However, I would like upload the file directly so curious how to do this as I am now but using Carrierwave_direct.

Any help appreciated.

NoMethodError (undefined method `has_key?')

Got local storage working with carrierwave 0.6.1. As soon as I add carrierwave_direct 0.0.5 to my gemset, without changing anything else, I get the following error. Adding the 'fog' gem, setting 'storage :fog', using version 0.0.4--nothing helps.

Started POST "/patients/101/documents" for 127.0.0.1 at 2012-04-04 15:43:13 -0700
Processing by Patients::DocumentsController#create as HTML
Parameters: {"utf8"=>"โœ“", "authenticity_token"=>"NMXO5l1kdcXpgDYbXZMtdz2fuDJKY+kTXJyFux/iEGw=", "document"=>{"description"=>"Document type 1", "location"=>#<ActionDispatch::Http::UploadedFile:0x000001022a7080 @original_filename="Portfolio Totals.csv", @content_type="text/csv", @headers="Content-Disposition: form-data; name="document[location]"; filename="Portfolio Totals.csv"\r\nContent-Type: text/csv\r\n", @tempfile=#File:/var/folders/W7/W7gnIpGEHbS8Tpkaf2MYDk+++TI/-Tmp-/RackMultipart20120404-52517-cw17h6>}, "commit"=>"Upload document", "patient_id"=>"101"}
User Load (0.3ms) SELECT users.* FROM users WHERE users.id = 65 LIMIT 1
Patient Load (1.4ms) SELECT patients.* FROM patients WHERE patients.id = 101 LIMIT 1
(0.6ms) BEGIN
(0.4ms) ROLLBACK
Completed 500 Internal Server Error in 95ms

NoMethodError (undefined method has_key?' for /uploads/tmp/20120404-1543-52517-5403/Portfolio_Totals.csv:LocationUploader): app/controllers/patients/documents_controller.rb:35:increate'

class Patients::DocumentsController < ApplicationController
def create
@document = Document.new(params[:document])
if @document.save # THIS IS LINE 35
redirect_to patient_documents_path(@patient, :notice => "Document created.") and return
else

... setup for new again...

render :action => "new" and return
end
end

class Document < ActiveRecord::Base
belongs_to :patient
validates_presence_of :description
mount_uploader :location, LocationUploader
attr_accessible :patient_id, :description, :location
end

class LocationUploader < CarrierWave::Uploader::Base
def store_dir
"uploads/#{model.id}"
end
end

class CreateDocuments < ActiveRecord::Migration
def self.up
create_table :documents do |t|
t.references :patient
t.string :description, :limit => 40
t.string :location
t.timestamps
end
end
end

Policy Condition failed: ["starts-with", "$key", "uploads"] when updating existing image

Hello there,

I'm able to upload images with carrierwave_direct the first time. But if I try to update them later on (change the image for another one), I get this S3 error:

<Error>
  <Code>AccessDenied</Code>
  <Message>
    Invalid according to Policy: Policy Condition failed: ["starts-with", "$key", "uploads"]
  </Message>
  <RequestId>[...]</RequestId>
  <HostId>[...]</HostId>
</Error>

My current setup is:

rails (3.2.12)
carrierwave (0.8.0)
carrierwave_direct (0.0.11)
fog (1.10.0)

Models:

class User < ActiveRecord::Base
  has_one :avatar, :as => :avatar_owner
end

class Avatar < ActiveRecord::Base
  belongs_to :avatar_owner, :polymorphic => true
  mount_uploader :image, AvatarUploader
end

Uploader (simplified):

class AvatarUploader < CarrierWave::Uploader::Base
  include CarrierWaveDirect::Uploader
  include Sprockets::Helpers::RailsHelper
  include Sprockets::Helpers::IsolatedHelper
  process :set_content_type
  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

Avatar form (I only allow creating avatars for existing users; when a user is created its avatar is nil):

<%= direct_upload_form_for @uploader do |f| %>
  <%= f.file_field :image %>
<% end %>

Avatars controller:

class AvatarController
  before_filter get_avatar

  def edit
    @uploader = @avatar.image
    @uploader.success_action_redirect = url_for(['update_image', @avatar_owner,'avatar'])
  end

  # successful uploads redirect here
  def update_image
    @owner.build_avatar if @owner.avatar.nil?
    @owner.avatar.key = params[:key] if params[:key]
    redirect_to :owner
  end

  private
  def get_avatar
    owner_type    = params[:type]
    owner_model   = owner_type.constantize
    @avatar_owner = owner_model.find params[:id]
    @avatar       = @avatar_owner.avatar || Avatar.new(:avatar_owner_id => @avatar_owner.id, :avatar_owner_type => owner_type)
  end
end

As a reminder: my problem is that I can upload an avatar image the first time, but any subsequent attempts at updating the image give me an error. I'd appreciate any help.

Thanks a lot!

Support CORS in S3?

Now that S3 Supports CORS could carrierwave_direct support direct uploading to s3 without a proxy?

RuntimeError with FactoryGirl

I'm having issues migrating my tests from CarrierWave to CarrierWaveDirect:

# user.rb

class User < ActiveRecord::Base
  mount_uploader :avatar, AvatarUploader

  def full_name
    return "#{first_name} #{last_name}"
  end
end

# avatar_uploader.rb

class AvatarUploader < CarrierWave::Uploader::Base
  include CarrierWaveDirect::Uploader

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
end

# factories.rb

include CarrierWaveDirect::Test::Helpers

FactoryGirl.define do
  factory :user do
    first_name Faker::Name.first_name
    last_name Faker::Name.last_name
    key { sample_key AvatarUploader.new }
  end
end

# user_test.rb

class UserTest < ActiveSupport::TestCase
  test "derives full name" do
    user = create :user

    assert_equal "#{user.first_name} #{user.last_name}", user.name
  end
end
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
app/uploaders/avatar_uploader.rb:21:in `store_dir'
   18:   # Override the directory where uploaded files will be stored.
   19:   # This is a sensible default for uploaders that are meant to be mounted:
   20:   def store_dir
=> 21:     "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
   22:   end
   23: 
   24:   # Provide a default URL as a default if there hasn't been a file uploaded:

The same exception is also reproducible in the console:

> include CarrierWaveDirect::Test::Helpers
> sample_key AvatarUploader.new

uninitialized constant ImageUploader::CarrierWaveDirect

I used RailsCasts' Carrierwave tutorial to get my image to upload and display, and now I wanted to get the images to upload directly to S3 with CarrierWaveDirect, but "uninitialized constant ImageUploader::CarrierWaveDirect" this error shows.
(I don't really know where the images were stored with just the CW, but I'm guessing inside the application)

After the Railscasts' version,

  1. I installed gem 'carrierwave_direct', and 'fog'
  2. I created a carrierwave.rb file inside the config/initializers (not sure if this is the right place or right file name because the readme in CarrierWave mentioned something about lib/carrierwave/storage/fog.rb)
  3. I changed the "storage:file" inside my image_uploader.rb to "include CarrierWaveDirect::Uploader"
  4. In the form view, I changed "form_for" to "direct_upload_form_for"

Do I need to change anything else? I also didn't really understand what
"Make sure you are loading CarrierWave after loading your ORM, otherwise you'll need to require the relevant extension manually, e.g.: require 'carrierwave/orm/activerecord' " meant in the original CW readme.

I am just confused what I need to do because both the original CW and CW_direct readme's mention S3 uploading, and I am caught in between the two directions that both are giving...

Can anyone please help me understand what's wrong and why it says 'uninitialized constant ImageUploader::CarrierWaveDirect'? I also had an error saying something about unauthentic, and I was assuming it had something to do with Devise - is that because the image is being attached to the user generated by Devise and I need to configure something?

Save in the BD

Hi, I need help

My problem is that upload field not save in the db

db:sqlite3

Atte Ernesto De Unanue T

PD Im sorry for my English

incorrect uploader-key, Rails 3.1+

Hi,

when I generate a direct upload form using your form builder, the field with the uploader key appears like this:

where "clip" is the class and with "uploads/#{model.class.to_s.underscore}/#{mounted_as}" in clip_uploader.rb

If I submit the form, the file is uploaded in the directory uploads/clip/name/27aedf10-2e58-012f-0aad-0025bce64796/${filename} but there is no redirect to my server after the upload.

Any idea what can be wrong?

Thanks.

how to update using the S3 "key" after an object has been created?

From the railscast and this document, I see how you would use carrierwave direct when you upload
the file to S3 first, and then create the object with the "key" value (from S3).

I would like to create the object first (saved to database) and then upload the file to S3.
My question is: how should I update the object with the returned key from S3?

Thanks

Handling filenames with square brackets in them.

Currently, if a filename has square brackets "[" or "]" in it, the direct_fog_url method throws and error saying

URI::InvalidComponentError: bad component(expected absolute path component)

The solution is to replace this line:

uri.path = URI.encode(path)

with

uri.path = URI.escape(URI.escape(path),"[]")

in "def direct_fog_url(options = {})" in uploader.rb

Not using a proc in translation

Hi!

We're using i18n database backend and the use of a proc removes the compatibility with that gem. I've noticed the proc you're using is related to validating the filename and type.

Are you open to a refactor on that?

Cheers!

upload generator executed in rails 3.2.6 engine

Generated uploader class does not work from inside a Rails 3 standalone engine. What I did:

  1. Configured carrierwave gem 0.6.2 in a rails engine Gemfile; bundle install
  2. ran rails g uploader leadFileUploader

The generated file was created in app/uploaders/<file.rb>
The generated source had a Class ... end definition

Instantiating this class had all kinds of weird errors until I did the following:

  1. move the generated file from app/uploaders/<file.rb> to app/uploaders//<file.rb>
  2. modified the generated source to place the class inside the engine module namespace:
    Module
    Class FileUploader
    ...
    end
    end

Breaks version URL format versus vanilla CarrierWave

We've been using vanilla (non-direct) CarrierWave in production for a while. Recently I tried using this gem for an upload form. After including the module in the Uploader class, the URLs for the various versions of my existing images are now broken. It seems that when this gem's module is included, the generated version key format looks like "blahblah_versionName.jpg", while with vanilla CarrierWave they look like "versionName_blahblah.jpg".

In the end it wasn't a huge deal to fix (had to recreate_versions! for all the images and save the AR model) but nevertheless this was a nasty surprise. Is there a reason why this gem needs to change the versions' key format?

Implementing unique file names

Hi,
How would one go about implementing unique file names.
In carrierwave one can just define
def filename

generate unique file name

end
However, with carrierwave direct the file is being uploaded directly from the front end.

Any ideas?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.