1

I want to build a simple File Upload system for my website (only I will be accessing and uploading) to upload my Portfolio Page. My website is on Ruby on Rails, hosted on Heroku.

So I was following the Heroku Tutorial to upload Images to S3. It uses the aws-sdk gem After following through the tutorial, when I try to upload a simple .png file, I received the following error.

Bad Request 400: Bucket POST must contain a field named 'key'.  If it is specified, please check the order of the fields.

PortfolioController

 def new
    @s3_direct_post = S3_BUCKET.presigned_post(key: "${filename}", success_action_status: 201, acl: :public_read)
    @portfolio = Portfolio.new()
  end

Checking the javascript formData value in the View:

...
fileInput.fileupload({
                formData:       '<%=@s3_direct_post.fields.to_json.html_safe %>',
                fileInput:      fileInput,
                url:            '<%=@s3_direct_post.url%>',
                type:           'POST',
                autoUpload:     true,
                paramName:      'file',
                dataType:       'XML',
                replaceFileInput: false,

...

gives:

{
"AWSAccessKeyId"=>"my-access-key",
"key"=>"${filename}",
"policy"=> "long-string",
"signature"=>"randomg-signature-string", 
"success_action_status"=>"201",
"acl"=>"public-read"
}

I've tried adding to sync my timing as shown in /config/initializers/aws.rb:

AWS.config(access_key_id:     ENV['AWS_ACCESS_KEY_ID'],
           secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'])

AWS::S3.const_set('DEFAULT_HOST', "s3-ap-southeast-1.amazonaws.com")

S3_BUCKET = AWS::S3.new.buckets[ENV['S3_BUCKET']]

After looking through Google and Stackoverflow, it seems that Jquery might be rebuilding the form data, hence messing up the order of the POST values.

Problem is, I'm relatively new to Ruby on Rails and Javascript, so I'm not sure how to go about fixing this.

Any advice is appreciated. Thanks!

1
  • 1
    I ran into the same issue, I decided to surround the image upload with a form and include all the necessary parameters (key, policy, acl etc) as hidden input elements. Then remove the formData: '<...>' line from the js file, and the plugin will use the form data in the order on the page. I ran into one issue with the signature being blank on the form, but since I was able to get the @s3_direct_post.fields onto the page I filled the input with the signature from there before submitting the image upload. Commented Feb 2, 2015 at 14:18

3 Answers 3

1

I was dealing recently with the same issue and this is the solution :

You have only to change this

formData:  '<%=@s3_direct_post.fields.to_json.html_safe %>'

To this :

formData:  <%=@s3_direct_post.fields.to_json.html_safe %>

Because @s3_direct_post.fields.to_json.html_safe will give you something like this :

{"key":"value", "key":"value", "key":"value"}

and by wrapping that between ' ' it will make it like this :

'{"key":"value", "key":"value", "key":"value"}'

Which is not a valid JSON

Another solution (but not efficient because the first is more elegant I think)

By looking at jquery file upload documentation it says :

By default, the plugin calls jQuery's serializeArray method on the upload form to gather additional form data for all input fields (including hidden fields)

So you can add those fields to the form (before the file input) like this :

<% @s3_direct_post.fields.map do |name, value| %>
    <input type="hidden" name="<%= name %>" value="<%= value %>" />
<% end %>

<%= f.file_field :avatar_url%>
...
...

But BE AWARE because as the documentation tell you it will gather all fields including hidden fields (in you Rails app there are some additional hidden fields like utf8 and authenticity_token) which will gives you an error because Amazon S3 won't accept that!

This is a little hack I did to solve that :

Make a separate form than your initial form, and assign an id to it, in my case the id is fields-for-s3

<form id="fields-for-s3">
    <% @s3_direct_post.fields.map do |name, value| %>
        <input type="hidden" name="<%= name %>" value="<%= value %>" />
    <% end %>
</form>
<%= form_for @user, ..... %>
    ....
<% end %>

Then use jQuery serializeArray() method on the form (with the specific ID) to create a formData object manually like this :

formData: $('form#fields-for-s3').serializeArray()

I hope this help.

Sign up to request clarification or add additional context in comments.

Comments

0

I decided to follow the Paperclip Heroku Tutorial instead, and was successfully able to upload to S3.

Hope this helps anyone with the same issue.

Comments

0

I ran into the same problem: You could also call JSON.parse(formdata); From your javascript.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.