13

I'm using dropzone with S3 and carrierwave. I'm able to upload images through Google Chrome, but I can't get it to work with Safari, which is weird.

This is my form

= nested_form_for @trip, html: { multipart: true, id: 'fileupload', class: 'directUpload', data: { 'form-data' => (@s3_direct_post.fields), 'url' => @s3_direct_post.url, 'host' => URI.parse(@s3_direct_post.url).host } } do |f|
  .dropzone#imageUpload
    = f.simple_fields_for :trip_images, TripImage.new, child_index: TripImage.new.object_id do |ff|
    = ff.file_field :photo, class: 'hide form-fields'
    = f.button :submit, id: "submit-data"

This is in the Trip controller

def set_s3_direct_post
  @s3_direct_post = S3_BUCKET.presigned_post(key: "/uploads/temporary/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read', content_type: 'image/jpeg')
end

This is TripImage model

class TripImage < ActiveRecord::Base
  belongs_to :resource, :polymorphic => true
  mount_uploader :photo, PhotoUploader
  after_create :process_async

  def to_jq_upload
    {
      'name' => read_attribute(:attachment_file_name),
      'size' => read_attribute(:attachment_file_size),
      'url' => attachment.url(:original),
      'thumbnail_url' => attachment.url(:thumb),
      'delete_url' => "/photos/#{id}",
      'delete_type' => 'DELETE'
    }
  end

  private

  def process_async
    PhotoVersioningJob.set( wait: 5.seconds ).perform_later(self.id)
  end

end

This is js

$(function(){
  $('.directUpload').find(".dropzone").each(function(i, elem) {
    s3ImageUpload(elem);
  });
})

function s3ImageUpload(elem){
  var fileInput    = $(elem);
  var form         = $(fileInput.parents('form:first'));
  var form_url = form.data('url');
  var form_data = form.data('form-data');
  Dropzone.options.imageUpload = {
    url: form_url,
    params: form_data,
    uploadMultiple: false,
    addRemoveLinks: true,
    removedfile: function(file){
      //some codes
    },
    success: function(file, serverResponse, event){
      //some codes
    },
    error: function(data){
      //some codes
    }
  };
}

EDIT: Current CORS configuration

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>*.example.com</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>PUT</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>*</AllowedHeader>
        <AllowedHeader>origin</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

Tested and doesn't work

EDIT: I also have S3 direct upload, not sure if this affects it too?

S3DirectUpload.config do |c|
  c.access_key_id = Rails::AWS.config['access_key_id']       # your access key id
  c.secret_access_key = Rails::AWS.config['secret_access_key']   # your secret access key
  c.bucket = Rails::AWS.config['bucket_name']              # your bucket name
  c.region = 's3'             # region prefix of your bucket url. This is _required_ for the non-default AWS region, eg. "s3-eu-west-1"
end
hellomello
  • 8,219
  • 39
  • 151
  • 297
  • I don't have access to safari but I imagine that the rows above the request could possibly contain calls not supported by safari. What data does each of the variables fileInput, form, form_url and form_data contain? – Pablo Jomer Dec 14 '15 at 14:41
  • Also what data do those variables contain when using chrome? – Pablo Jomer Dec 14 '15 at 14:42
  • How does it fail in Safari? See any errors in the console? – deprecated Dec 14 '15 at 15:31
  • @PabloKarlsson so I was able to look in console and it does pass temporary amazon aws url parameters in `trip_images_attributes`, but I'm just wondering why it doesn't save the way chrome saves? – hellomello Dec 15 '15 at 06:19
  • @PabloKarlsson I noticed when I try it on chrome, it does a INSERT INTO after showing the parameters, however, in Safari, this doesn't happen – hellomello Dec 15 '15 at 06:27
  • @vemv no errors, it just doesn't doesn't produce an INERT INTO statement like chrome does – hellomello Dec 15 '15 at 06:27
  • 1
    `INSERT INTO` is the last step, try to debug the previous steps. Are the right requests sent from Safari, or no requests at all? If yes, how different are the parameters when being sent from Chrome and Safari? – deprecated Dec 15 '15 at 13:17
  • @vemv sorry, do you think you can give me ideas of how I should debug? The parameters are the same, the only thing missing is the INSERT INTO in safari when I do `heroku logs` – hellomello Dec 16 '15 at 03:35

1 Answers1

6

I have run into a similar issue with Safari recently, and discovered that it is sending an extra Access-Control-Request-Header that Chrome does not -- specifically 'origin'. To address this difference I needed to update my AWS CORS config on the destination bucket.

AWS Documentation on the necessity of request headers matching an allowed header config. The third bullet point makes this requirement explicit:

Every header listed in the request's Access-Control-Request-Headers header on the preflight request must match an AllowedHeader element.

This helpful StackOverflow answer gives an example config:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>Authorization</AllowedHeader>
    <AllowedHeader>x-requested-with</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

And what needed to be added to get it working in Safari:

    <AllowedHeader>origin</AllowedHeader>
Community
  • 1
  • 1
craigts
  • 2,857
  • 1
  • 25
  • 25
  • I was hoping that'll do the trick. I updated my question to show what my CORS configuration is. Is that what its supposed to look like? – hellomello Dec 16 '15 at 07:26
  • Also, I'm not sure if its CORS issue? I'm looking at heroku logs and it doesn't INSERT INTO the database. This happens in Chrome though. – hellomello Dec 16 '15 at 07:33
  • @hellomello yes, assuming *.example.com is your website hostname in reality. Also you will need to make sure you're not getting a cached version of the pre-flight response which in your case will stick around for at most 3000 seconds. If it is still not working for you I can only think of one other issue we had which was trying to call an https url from an http page or the reverse. I'm not an expert in the stack you're using, but I have quite a bit of experience with direct s3 uploads. The only reason I made this an answer is that it presented exactly the same way (not working in safari only) – craigts Dec 16 '15 at 07:40
  • @hellomello is the file getting into the s3 bucket? – craigts Dec 16 '15 at 07:50
  • No, its not being saved into the bucket. I can see in heroku logs that the form is producing the amazon URLs in the form parameters, i.e.: `https://.s3.amazon.aws.com/%2Fuploads%2Ftemporary%2Frandomnumbers.jpg`, but when I'm in the bucket, its not there. Its not saving into database records as well, I suppose. the urls are https, my site is http. – hellomello Dec 16 '15 at 07:58
  • I would check that out. Also one gotcha with s3 and https is that if you have a dot(.) in your bucket name then the https cert won't validate. I don't think that is happening in your case since you would likely see it failing for all browsers. If you do have a dot in your bucketname you have to use this style url: `https://s3.amazonaws.com/mybucket.name/myObjectKey` – craigts Dec 16 '15 at 08:06
  • There's no dot in my bucket name. Do I need to add the Authorization and x-requested-with? – hellomello Dec 16 '15 at 08:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/98145/discussion-between-craigts-and-hellomello). – craigts Dec 16 '15 at 16:53
  • I tried joining the chat, but no luck in reaching you. – hellomello Dec 18 '15 at 06:03
  • I figured out that it is a CORS issue. When I tried to add the headers: `authorization`, `x-requested-with`, `origin`, it doesn't work for firefox anymore (I didn't try google). When I removed these and just kept the `*` it worked again. – hellomello Dec 21 '15 at 01:30
  • I am uploading 8 files at a time on S3 through rails application. But after uploading all files, I am not getting callback in the dropzone `succcess` block. But if I upload 4 files then it is working. I spent 2 days but still not getting any solution. Please help. – Parthiv Sep 26 '17 at 07:14