Home>
How to upload images directly from Rails to S3 on AWS

We have implemented a mechanism to upload images directly from Rails to S3 on AWS.
So one question is, the implementation itself was completed without any problems, but the images are not uploaded from the local development environment to S3 on AWS.

We are considering using Heroku for the production environment, but currently it does not work in the local development environment.

Reference site: https://qiita.com/DaichiSaito/items/80e89f0c96d88afcc5ff
Reference site: https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails

Below is the actual code.

GemfileFg
gem'aws-sdk','~>2'
model/users.rb
class User</pre>
<pre><code>class UserAvatar</pre>
<pre><code>users_controller.rb
before_action: set_s3_direct_post, only: [: new,: edit,: create,: update] # Add
#User new registration form
  def new
    if current_user
      redirect_to current_user
    else else
      @user_form = UserForm.new
    end
  end
  #User edit form
  def edit
    @user_form = UserForm.new (User.find (params [: id]))
  end
  #Create new user
  def create
    @user_form = UserForm.new
    @ user_form.assign_attributes (params [: form])
    if @ user_form.save
      @ user_form.user.send_activation_email
      redirect_to: signup_thanks
    else else
      render: new
    end
  end
  # User update
  def update
    @user_form = UserForm.new (@user)
    @ user_form.assign_attributes (params [: form])
    if @ user_form.save
      redirect_to @ user_form.user
    else else
      render: edit
    end
  end
private
    # add to
  def set_s3_direct_post
    @ s3_direct_post = S3_BUCKET.presigned_post (key: "uploads/# {SecureRandom.uuid}/${filename}", success_action_status: '201', acl:'public-read')
  end
forms/user_form.rb
class UserForm
  include ActiveModel :: Model
  attr_accessor: user,: name,: email,: gender,: inputs_destroy
  delegate: new_record ?,: persisted ?,: save, to:: user
  def initialize (user = nil)
    @user = user
    @user || = User.new
    @ user.build_avatar unless @ user.avatar.present?
  end
  def assign_attributes (params = {})
    @params = params
    user.assign_attributes (user_params)
    if params [: user] [: avatar] .present?avatar = avatar_params.fetch (: avatar)
      user.avatar.assign_attributes (avatar)
    end
  end
  private
  def user_params
    @ params.require (: user) .except (: avatar) .permit (
      : name,: email,: gender,: profile_text,: password,: password_confirmation
    )
  end
  def avatar_params
    @ params.require (: user) .slice (: avatar) .permit (
      avatar: [: image]
    )
  end
end
views/users/edit.html.erb<% = form_for (@user_form, as:'form', url: @ user_form.user, html: {class:'directUpload', data: {'form-data' =>(@ s3_direct_post.fields),'url' =>@ s3_direct_post.url,'host' =>URI.parse (@ s3_direct_post.url) .host}}) do | f |%># Modify<script>$(function () {
  $('.directUpload'). find ("input: file"). each (function (i, elem) {
    var fileInput = $(elem);
    var form = $(fileInput.parents ('form: first'));
    var submitButton = form.find ('input [type = "submit"]');
    var progressBar = $(" ");
    var barContainer = $(" "). append (progressBar);
    fileInput.after (barContainer);
    fileInput.fileupload ({{
      fileInput: fileInput,
      url: form.data ('url'),
      type:'POST',
      autoUpload: true,
      formData: form.data ('form-data'),
      paramName:'file', // S3 does not like nested name fields i.e. name = "user [avatar_url]"
      dataType:'XML', // S3 returns XML if success_action_status is set to 201
      replaceFileInput: false,
      progressall: function (e, data) {
        var progress = parseInt (data.loaded/data.total * 100, 10);
        progressBar.css ('width', progress +'%')
      },


      start: function (e) {
        submitButton.prop ('disabled', true);
        progressBar.
          css ('background','green').
          css ('display','block').
          css ('width', '0%').
          text ("Loading ...");
      },


      done: function (e, data) {
        submitButton.prop ('disabled', false);
        progressBar.text ("Uploading done");
        // extract key and generate URL from response
        var key = $(data.jqXHR.responseXML) .find ("Key") .text ();
        var url ='//' + form.data ('host') +'/' + key;
        // create hidden field
        var input = $("<input />", {type:'hidden', name: fileInput.attr ('name'), value: url})
        form.append (input);
      },


      fail: function (e, data) {
        submitButton.prop ('disabled', false);
        progressBar.
          css ("background", "red").
          text ("Failed");
      }
    });});
});</script>
config/initializers/aws.rb
Aws.config.update ({{
  region:'ap-northeast-1',
  credentials: Aws :: Credentials.new (
    '××××××××××××××××××××××××××××××××', # Access key
    '××××××××××××××××××××××××××××××××' # Secret Key
  ),
})
S3_BUCKET = Aws :: S3 :: Resource.new.bucket ('×××××××××××××××××××××') # bucket name

Download the following two libraries and install them under app/assets/javascripts
https://raw.githubusercontent.com/jquery/jquery-ui/master/ui/widget.js
→ app/assets/javascripts/jquery.ui.widget.js

https://raw.githubusercontent.com/blueimp/jQuery-File-Upload/master/js/jquery.fileupload.js
→ app/assets/javascripts/z.jquery.fileupload.js

I implemented it as above, but when I upload the image, the parameters are as follows and it is not uploaded to AWS S3.

Parameters: {"utf8" =>"✓", "authenticity_token" =>"hqGryq1RfJW1tv4XzWOK8xLs3Dazxs3h0u8 + BScjb5N620ynmSg5Xnc5keXHl8QryVY4bolxrMAujB0vdfISTw ==" {">ActionDispatch :: Http :: UploadedFile: 0x00007fa188674298 @tempfile = #<Tempfile:/var/folders/gd/52ml8dfd03v0jfgy6r2ls4x80000gn/T/RackMultipart20201103-12508-1pra42j.jpg>, @ original_filename = "2018-06-29 12.13.jpg. ", @content_type =" image/jpeg ", @ headers =" Content-Disposition: form-data;name = \ "form [user] [avatar] [image] \";filename = \ "2018-06-29 12.13 .08.jpg \ "\ r \ nContent-Type: image/jpeg \ r \ n">},

 "name" =>"test01", "email" =>"[email protected]", "profile_text" =>"" "}},

 "commit" =>"Update with the above", "id" =>"1"}
↳ app/forms/user_form.rb: 5
  User Update (0.2ms) UPDATE "users" SET "profile_text" = $1, "updated_at" = $2 WHERE "users". "Id" = $3 [["profile_text", ""],

 ["updated_at", "2020-11-03 08: 50: 40.670585"],

 ["id", 1]]
  ↳ app/forms/user_form.rb: 5
  UserAvatar Create (0.6ms) INSERT INTO "user_avatars" ("user_id", "image", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["user_id", 1] ,

 ["image", "# "],

 ["created_at", "2020-11-03 08: 50: 40.671752"],

 ["updated_at", "2020-11-03 08: 50: 40.671752"]]
  ↳ app/forms/user_form.rb: 5
   (0.5ms) COMMIT

The source code is problematic because the image part contains # I don't think so.

CORS was set as follows from the AWS management screen.

[
    {
        "AllowedHeaders": [
            "*"
        ],,


        "AllowedMethods": [
            "GET",
            "PUT",
            "POST"
        ],,


        "AllowedOrigins": [
            "http: // localhost: 3000"
        ],,


        "Expose Headers": [],


        "MaxAgeSeconds": 3000
    }
]

I would appreciate it if you could tell me the solution. I look forward to working with you.

Reference site: https://qiita.com/DaichiSaito/items/80e89f0c96d88afcc5ff
Reference site: https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails

  • Answer # 1

    // = require jquery.ui.widget
    // = require z.jquery.fileupload


    It was solved by describing the above.

Related articles