Rails direct to S3 upload using aws-sdk gem and jQuery-File-Upload on heroku

前端 未结 2 1852
日久生厌
日久生厌 2020-12-21 01:09

I\'m trying to achieve direct to Amazon S3 upload in Rails using jQuery-File-Upload and the aws-sdk gem, and following heroku\'s direct to S3 upload instructions. This is th

相关标签:
2条回答
  • 2020-12-21 01:40

    Finally found the answer here. Simply had to go to application.js and change

    //= require jquery-fileupload
    

    to

    //= require jquery-fileupload/basic
    

    Christ on a tandem. Just pissed away 50 rep points on getting a whole 2 more views.

    0 讨论(0)
  • 2020-12-21 01:55

    This code uses aws-sdk-v2 for ruby, it's been submitted to Heroku to update their documentation on s3 direct uploads (https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails#pre-signed-post)

    Some aspects of this are not necessary, i.e. storing your own photo object in the DB after uploading to s3. And the code is obviously not as good as it could be, this was just to get it working. I'm happy to hear any suggestions to improve it or know if something here isn't working for you.

    The Sidekiq job has a method do_work which is a class I use defined in BaseJob to give me some additional metrics and info you can't get without SidekiqPro, so you would need to change this to "perform" if you're not doing anything special with the Sidekiq perform class.

    #ROUTE CODE
    post 'create_photo' => 'users#create_photo'
    
    #CONTROLLER CODE
    
    #intial view, might be update or another method in your case
    def new
      @user = current_user.find params[:user_id]            
    
      @s3_direct_post = Aws::S3::Bucket.new(name: ENV['aws_bucket']).presigned_post(key: "musea_upload/harrisjb/${filename}", success_action_status: "201", acl: "public-read") 
    end
    
    #called from ajax method in code below
    # I store a photograph object with an id and s3 url in my DB
    # this code calls a sidekiq job in the user model to create 
    # the photo object in the DB after the photo is uploaded directly to s3
    # not essential, but helpful to reference uploaded photos
    
    def create_photo                                                              
      @user = current_user.find params[:user_id]            
      options = {}                                                                
      options['user_id'] = params[:user_id]                                     
      options['key'] = params[:key]                                               
      options['url'] = params[:url]                                               
      @user.create_photograph_from_s3(options)                                   
    
      respond_to do |format|                                                      
        format.js { }                                                             
       end                                                                         
    end  
    
    #MODEL CODE
    def create_photograph_from_s3(options)
      CreatePhotographJob.perform_async(options)
    end
    
    #JOB CODE
      def do_work(options)                                                          
        user_id = options['user_id']                                              
        user = User.find_by(id: user_id)                                                
        Rails.logger.info "CREATE PHOTOGRAPH JOB FOR USER #{user_id}"             
        url = options['url'].gsub("//h", "h")                                       
        user.create_photograph_from_file(url)                                
        Rails.logger.info "PHOTOGRAPH CREATED"                                      
      end 
    
    
    #VIEW CODE
    <%= form_for(:user_avatar, :remote => true, :url => p_user_avatar_upload_to_s3_path(user_id: @user.id), html: { id: "uploader", class: "uploader white-bg" }) do |f| %>                                                                                
      <input name="authenticity_token" type="hidden" value="<%= form_authenticity_token %>">
      <%= f.file_field :photo, multiple: true, style: 'margin-top: 20px; height: 5em; width: 100%; padding-top: 5%; padding-left: 20%;', class: 'form-group form-control light-gray-bg' %>                                       
    <% end %>                         
    
    # JS -- if you want this in your view use the content_for
    # or you can store the functions in application.js or wherever you want
    
    <% content_for :javascript do %>
      <script>  
          $(function() {
            $('.uploader').find("input:file").each(function(i, elem) {
              var fileInput    = $(elem);
              var form         = $(fileInput.parents('form:first'));
              var photos       = $('#photo-list');
              var photo_errors = $('#photo-list-errors');
              var photo_name   = 'photo_' + i;
              var submitButton = form.find('input[type="submit"]');
    
              //taken from heroku example, some bootstrap specific CSS here as well
              var progressBar  = $("<div class='progress-bar progress-bar-success progress-bar-striped active style='width: 0%;'></div>");
              var barContainer = $("<div class='progress col-md-12 col-sm-12 col-xs-12' style='margin-top: 20px; padding-left: 0px; padding-right: 0px;'></div>").append(progressBar);
              fileInput.after(barContainer);
    
              //jquery direct upload do its thing
              fileInput.fileupload({
                url:             '<%= @s3_direct_post.url %>',
                type:            'POST',
                autoUpload:       true,
                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,
                formData:         <%= @s3_direct_post.fields.to_json.html_safe %>,
                fileInput:       fileInput,
                progressall: function (e, data) {
                  var progress = parseInt(data.loaded / data.total * 100, 10);
                  progressBar
                    .text(progress + '%')
                    .css('role', 'progressbar')
                    .css('width', progress + '%');
    
                },
    
                start: function (e) {
                  $('.spinner').removeClass('hidden');
                  $('.progress-bar').addClass('progress-bar-striped');
                  submitButton.prop('disabled', true);
                },
    
                // Called when all files are uploaded
                stop: function (e) {
                  $('.progress-bar').removeClass('progress-bar-striped');
                  $('.progress-bar').html('Done!');
                },
    
                //
                done: function(e, data) {
                  $('.spinner').addClass('hidden');
                  submitButton.prop('disabled', false);
    
                  // extract key and generate URL from response
                  var key   = $(data.jqXHR.responseXML).find("Key").text();
                  var url   = '//<%= @s3_direct_post.url %>/' + key;
    
                  // when uploading entire folders dropped onto file_field
                  // ignore .DS_Store (common with iPhone photos, etc)    
                  if (key.indexOf(".DS_Store") >= 0 ) {
                    console.log("nope. not doing it.");
    
                  } else {
    
                    // this is showing a preview photo on the left side of the screen
                    // as photos are being uploaded
    
                      photos.prepend("<div class='photo col-md-2 col-sm-3 col-xs-3' style='margin-bottom: 10px; margin-top: 10px; background-image: url(<%= @s3_direct_post.url %>/" + key + ");  background-position: 50% 50%; background-size: cover;'><img class='photo_image' height='1' width='1' src='<%= @s3_direct_post.url %>/" + key + "'> </div>");
    
                    // actual post to your controller
    
                      $.ajax({ type: "POST",
                      url: "/users/<%=@user.id%>/create_user_avatar",
                      data: { authenticity_token: '<%= form_authenticity_token %>', user_id: '<%= @user.id %>', key: key, url: url },
                      success: function(data) { },
                      error: function(data) {
                        var jqxHR = data.jqXHR;
                        var status = data.textStatus;
                        var error_message = data.errorThrown;
                        var key = $(data.jqXHR.responseXML).find("Key").text();
                        photo_errors.append("<div class='col-md-11 col-sm-11 col-xs-11 headline_three' style='margin-top:10px; margin-right: 10px;'> There was an error uploading the photo " + key + " Error message: " + error_message + " Please attempt to upload this photo again.</div>");
                      }
                    }); //ajax call
    
                   } // if-else
    
                } // done function
    
              });
    
            });
          });
    
       </script>  
    <% end %>
    
    0 讨论(0)
提交回复
热议问题