I'm trying to achieve direct to Amazon S3 upload in Rails using 
jQuery-File-Upload <https://github.com/blueimp/jQuery-File-Upload> and the 
aws-sdk <https://github.com/aws/aws-sdk-ruby> gem, and following heroku's 
direct to S3 
<https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails> 
instructions. 
This is the upload form produced in the html:

    <form id="pic-upload"
    class="directUpload" 
    data-form-data="{
    "key":"uploads/59c99e44-6bf2-4937-9680-02c839244b33/${filename}",
    "success_action_status":"201",
    "acl":"public-read",
    "policy":"eyJle...In1dfQ==",
    "x-amz-credential":"AKIAJCOB5HQVW5IUPYGQ/20160101/us-east-1/s3/
aws4_request",
    "x-amz-algorithm":"AWS4-HMAC-SHA256",
    "x-amz-date":"20160101T010335Z",
    "x-amz-signature":"0f32ae...238e"}" 
    data-url="https://websmash.s3.amazonaws.com"; 
    data-host="websmash.s3.amazonaws.com"
    enctype="multipart/form-data"
    action="/users/bazley/update_pictures" 
    accept-charset="UTF-8" 
    method="post">

This is the corresponding jQuery:

    $(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  = $("<div class='bar'></div>");
        var barContainer = $("<div class='progress'></div>").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");
          }
        });
      });
    });

Trying to upload a file produces these logs:

    Started POST "/users/bazley/update_pictures" for ::1 at 2016-01-01 21:26
:59 +0000 Processing by CharactersController#update_pictures as HTML
    Parameters: {
        "utf8"=>"✓", 
        "authenticity_token"=>"rvhu...fhdg==",
        "standardpicture"=>{
            "picture"=>#<ActionDispatch::Http::UploadedFile:0x0000010b32f530 

                
@tempfile=#<Tempfile:/var/folders/19/_vdcl1r913g6fzvk1l56x4km0000gn/T/RackMultipart20160101-49946-7t94p.jpg>,
 

                @original_filename="europe.jpg", 
                @content_type="image/jpeg", 
                @headers="Content-Disposition: form-data; 
name=\"standardpicture[picture]\"; filename=\"europe.jpg\"\r\nContent-Type: 
image/jpeg\r\n">
        }, 
        "commit"=>"Upload pictures", 
        "callsign"=>"bazley"
    }

The form submits successfully, but it isn't working because Rails doesn't 
save the correct location ("picture", a string) on S3; instead it thinks 
the location is 

    "picture"=>#<ActionDispatch::Http::UploadedFile:0x0000010b32f530

You can see this in the submitted parameters. It should be something like: 

    "picture"=>
"//websmash.s3.amazonaws.com/uploads/220f5378-1e0f-4823-9527-3d1170089a49/europe.jpg"
}, "commit"=>"Upload pictures"}

What I don't understand is why it's getting the parameters wrong when all 
the correct information seems to be present in the form. It clearly says

    data-url="https://websmash.s3.amazonaws.com"; 

in the form, and the jQuery includes

    url:  form.data('url'),

so what's going wrong?

For completeness: in the controller:

    before_action :set_s3_direct_post
    .
    .
    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

The form:

    <%= form_for :standardpicture, url: update_pictures_user_path,
                 html: {  id: "pic-upload", class: "directUpload",
                          data: { 'form-data' => (@s3_direct_post.fields),
                                  'url' => @s3_direct_post.url,
                                  'host' => URI.parse(@s3_direct_post.url).host 
} 
                       } do |f| %>
      <div class="field">
        <%= f.label :picture %>
        <%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' 
%>
      </div>
      <%= f.submit "Upload pictures", class: "btn btn-primary" %>
    <% end %>

aws.rb initializer:

    Aws.config.update({
      region: 'us-east-1',
      credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV[
'AWS_SECRET_ACCESS_KEY']),
    })
    S3_BUCKET = Aws::S3::Resource.new.bucket(ENV['S3_BUCKET'])

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/rubyonrails-talk/0f1c1eb0-740e-4b41-9282-79b3b41cba34%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to