Question

I am trying to implement a direct to S3 upload system using the fine-uploader script and a rails server.

The problem I am facing is that the endpoint where I post in the event of S3 upload success (i.e. uploadSuccess endpoint) is a controller that renders a rails js.erb template, which supposedly should update the dom. The script is caught by the fine-upload script as a successful json response, but the js script is not executed as such.

The relevant code:

images_controller.rb

def create
  @image = Image.create!
  set_standard_vars
end

create.js.erb (view)

alert("This is the view");
$("div#image_name").attr("id", <%= @image.name %>);

uploader.js

$(function () {
'use strict';

$("div#button").fineUploaderS3({
    debug: true,
    uploaderType: 'basic',
    button: $('div#button'),
    validation: {
      allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
      sizeLimit: 3000000 // 3MB
    },
    objectProperties: {
      key: function(fileId) {
          var filename = $("div#button").fineUploader("getName", fileId);
          return item_picture_bucket + "/" + filename;
      },
      acl: "public-read"
    },
    request: {
        endpoint: "mybucket.s3.amazonaws.com",
        accessKey: access_key
    },
    signature: {
        endpoint: signature_end_point,
        customHeaders: {
            'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
        }
    },
    uploadSuccess: {
        endpoint: upload_url,
        customHeaders: {
            'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
        }
    },
  })
});

Notice that upload_url is set to be image#create. The create.js.erb template is rendered correctly by rails but the script interprets it as json, as opposed to javascript to be streamed to the browser, and therefore it does not display the alert box.

Thanks in advance for your help.

Was it helpful?

Solution 2

I think I found a reasonable rails solution to this problem.

In the images_controller.rb, instead of rendering the template as javascript, render the template as text using the render_to_string method and incapsulate it into the json response:

def create
  # create! is really to simplify the example, in reality you want to use a background worker to 
  # attach the image to a model and maybe resize it, but this is not the point of the question
  @image = Image.create! 
  set_standard_vars
  render :json => { :success => true, :partial => render_to_string }
end

This will send the rendered create.js.erb template back to fineuploader into the json response. I then run the script using the eval JS method and the "on complete" callback:

$(function () {
    'use strict';

    $("div#button").fineUploaderS3({
        debug: true,
        uploaderType: 'basic',
        button: $('div#button'),
        validation: {
          allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
          sizeLimit: 3000000 // 3MB
        },
        objectProperties: {
          key: function(fileId) {
              var filename = $("div#button").fineUploader("getName", fileId);
              return item_picture_bucket + "/" + filename;
          },
          acl: "public-read"
        },
        request: {
            endpoint: "anglers-zoo.s3.amazonaws.com",
            accessKey: access_key
        },
        signature: {
            endpoint: signature_end_point,
            customHeaders: {
                'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
            }
        },
        uploadSuccess: {
            endpoint: upload_url,
            customHeaders: {
                'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
            },
            params: {
              'hash': image_group,
              'position': $('#button_slot').data('imageposition'),
            }
        },
      }).on("submit", function(id, name) {
        $('.destroy_image').hide();
        $('#button').hide();
        $("div#button").parent().find('#throbber').show();
        disable_submit_button();
      }).on("complete", function(event, id, name, responseJSON, xhr){
        jQuery.globalEval(responseJSON.partial);
      });
    });

It is still not 100% the rails way, but it is clean and rails-sy enough for me.

OTHER TIPS

Although I've got no experience with fine uploader, I'll give you some ideas about Rails, considering the author took the time to reply promptly to you:


respond_to

#app/controllers/images_controller.rb
def create
  @image = Image.create!
  set_standard_vars
  respond_to do |format|
      format.html
      format.json { render json: {success: true} }
  end
end

If fine-uploader is processing the request as JSON, perhaps you'd be better to prepare a JSON response? This would allow you to at least handle the response with fine-uploader directly (rather like how ajax:success works):

$("div#button").fineUploaderS3({
    debug: true,
    uploaderType: 'basic',
    button: $('div#button'),
    validation: {
      allowedExtensions: ['jpeg', 'jpg', 'gif', 'png'],
      sizeLimit: 3000000 // 3MB
    },
    objectProperties: {
      key: function(fileId) {
          var filename = $("div#button").fineUploader("getName", fileId);
          return item_picture_bucket + "/" + filename;
      },
      acl: "public-read"
    },
    request: {
        endpoint: "mybucket.s3.amazonaws.com",
        accessKey: access_key
    },
    signature: {
        endpoint: signature_end_point,
        customHeaders: {
            'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
        }
    },
    uploadSuccess: {
        endpoint: upload_url,
        customHeaders: {
            'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
        }
    },
  })
})
.on('complete', function(event, id, fileName, responseJSON) {
       alert("Success: " + responseJSON.success);
       if (responseJSON.success) {
          alert("success");
       }
});

This would need to have the correct JSON returned

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top