2013-10-03 1 views
0

Я использую модифицированную версию Jquery Multi-file uploader из Railscast # 383 (http://railscasts.com/episodes/383-uploading-to-amazon-s3) в приложении Rails 3, и мне нужно настроить его так, чтобы он проверял, файл уже существует на S3 и пропускает повторную загрузку, если это так.Избегайте перезаписи файлов S3 с помощью Jquery-Fileupload

Некоторые предпосылки: моим пользователям необходимо обновить большие куски данных. Например, можно выбрать 500 файлов размером 4 МБ для загрузки. Неизбежно, их интернет-соединение ломается, и вместо того, чтобы ожидать, что пользователь выяснит, какие файлы загружены, а какие нет, я хочу, чтобы они могли просто выбрать те же 500 файлов, и приложение было достаточно умным, чтобы не запускать снова на самое начало.

Наиболее предпочтительным решением было бы включить опцию в S3 POST, которая говорит, что не перезаписывать существующий файл. Следующим наиболее предпочтительным было бы отключить GET до S3, чтобы увидеть, существует ли файл и пропустить его, если это так.

Наименее предпочтительно, что я реализовал решение, которое не асинхронно запускает GET в мое приложение Rails (поскольку я создаю запись базы данных после завершения каждой загрузки), но у меня, похоже, возникают проблемы с дросселированием этих запросов , и мой пользователь говорит, что ее браузер продолжает терпеть крах (это все 500 одновременно, я думаю).

Соответствующие application.js

//= require jquery 
//= require jquery_ujs 
//= require jquery.ui.all 
//= require jquery-fileupload/basic 
//= require jquery-fileupload/vendor/tmpl 

Моя форма:

<%= s3_uploader_form post: uploaded_photos_path, as: "uploaded_photo[image_url]", photo_shoot_id: @photo_shoot.id do %> 
    <%= file_field_tag :file, multiple: true %> 
    <%= button_tag 'Upload Photos', id: 'upload_photo_button', type: 'button' %> 
<% end %> 

Моя JavaScript:

$(function() { 
    $('#s3_uploader').fileupload({ 
    limitConcurrentUploads: 5, 
    add: function(e, data) { 
     var file, record_exists, photo_check_url; 
     file = data.files[0]; 
     photo_check_url = "/my_route/has_photo_been_uploaded/" + encodeURIComponent(file.name) 

     // THIS IS MY NON-THROTTLING HACK THAT NEEDS REPLACEMENT/IMPROVEMENT 
     // THE CONTROLLER THAT HANDLES THE REQUEST JUST RENDERS AN INLINE STRING OF 'true' OR 'false' 
     $.ajax({ 
     url: photo_check_url, 
     async: false, 
     success: function (result) { 
      record_exists = result; 
     } 
     }); 
     if (record_exists == 'false') { 
     data.context = $(tmpl("template-upload", file)); 
     $('#s3_uploader').append(data.context); 
     data.submit();   
     } 
    }, 
    progress: function(e, data) { // irrelevant }, 
    done: function(e, data) { // irrelevant. It posts the object to my database } 
    }, 
    fail: function(e, data) { // irrelevant } 
    }); 
}); 

Мой Helper:

module S3UploaderHelper 

    def s3_uploader_form(options = {}, &block) 
    uploader = S3Uploader.new(options) 
    form_tag(uploader.url, uploader.form_options) do 
     uploader.fields.map do |name, value| 
     hidden_field_tag(name, value) 
     end.join.html_safe + capture(&block) 
    end 
    end 

    class S3Uploader 

    def initialize(options) 
     @options = options.reverse_merge(
     id: "s3_uploader", 
     aws_access_key_id: ENV["S3_ACCESS_KEY"], 
     aws_secret_access_key: ENV["S3_SECRET_ACCESS_KEY"], 
     bucket: S3_BUCKET_NAME, 
     acl: "private", 
     expiration: 10.hours.from_now.utc, 
     max_file_size: 20.megabytes, 
     as: "file" 
    ) 
    end 

    def form_options 
     { 
     id: @options[:id], 
     method: "post", 
     authenticity_token: false, 
     multipart: true, 
     data: { 
      post: @options[:post], 
      as: @options[:as] 
     } 
     } 
    end 

    def fields 
     { 
     :key => key, 
     :acl => @options[:acl], 
     :policy => policy, 
     :signature => signature, 
     "AWSAccessKeyId" => @options[:aws_access_key_id], 
     } 
    end 

    def key 
     @key ||= "uploaded_photos/${filename}" 
    end 

    def url 
     "https://#{@options[:bucket]}.s3.amazonaws.com/" 
    end 

    def policy 
     Base64.encode64(policy_data.to_json).gsub("\n", "") 
    end 

    def policy_data 
     { 
     expiration: @options[:expiration], 
     conditions: [ 
      ["starts-with", "$utf8", ""], 
      ["starts-with", "$key", ""], 
      ["content-length-range", 0, @options[:max_file_size]], 
      {bucket: @options[:bucket]}, 
      {acl: @options[:acl]} 
     ] 
     } 
    end 

    def signature 
     Base64.encode64(
     OpenSSL::HMAC.digest(
      OpenSSL::Digest::Digest.new('sha1'), 
      @options[:aws_secret_access_key], policy 
     ) 
    ).gsub("\n", "") 
    end 
    end 
end 
+0

Или если есть способ дросселировать запуск события ADD, это тоже сработает для меня. – LikeMaBell

+0

Возможно, я асинхронно звоню в свою базу данных и добавляю крючок, который следит за «ложным» ответом, а затем запускает отправку (POST на S3)? Какие-нибудь советы о том, как будет выглядеть этот код? – LikeMaBell

ответ

0

После получения дополнительной информации об AJAX (после того, как это произошло со мной в моем втором комментарии), похоже, что приемлемым решением было сделать асинхронный вызов AJAX и поместить код SST POST в его успешный обратный вызов. Это решило проблемы, связанные с не-отзывчивом браузером.

$.ajax({ 
    url: my_route_to_ask_if_photo_was_already_uploaded, 
    success: function (result) { 
    if (result == 'false') { 
     // ...other code 
     data.submit();   
    } 
    }); 
Смежные вопросы