2013-10-03 41 views
0

我在Rails 3應用程序中使用來自Railscast#383(http://railscasts.com/episodes/383-uploading-to-amazon-s3)的Jquery多文件上傳器的修改版本,我需要調整它以檢查是否文件已經存在於S3上,如果存在,則跳過重新上傳。避免覆蓋S3文件使用Jquery-Fileupload

一些背景:我的用戶需要更新大塊的數據。例如,可以選擇500個4MB文件上傳。不可避免的是,他們的互聯網連接中斷,而不是期望用戶找出哪些文件上傳,哪些沒有上傳,我希望他們能夠選擇那些相同的500個文件,並且該應用足夠聰明,不會在一開始。

最好的解決方案是在S3 POST中包含一個選項,表示不覆蓋現有文件。接下來最好的方法是向S3發出GET以查看文件是否存在,如果存在則跳過它。

最好的是,我已經實現了一個解決方案,它非異步地向我的Rails應用程序發出GET請求(因爲我在每次上傳完成時創建了一個數據庫條目),但我似乎遇到了限制這些請求的問題,並且我的用戶說她的瀏覽器不斷崩潰(我猜想它一次可以完成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 } 
    }); 
}); 

我的助手:

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

也許我異步調用我的數據庫,然後添加一個監視'false'響應的鉤子,然後觸發提交(POST到S3)?有關代碼的外觀的任何提示? – LikeMaBell

回答

0

在瞭解了關於AJAX的更多信息之後(在我的第二個評論中發生在我身上之後),看起來好像一個可接受的解決方案的確是讓AJAX調用異步並將S3 POST代碼放入其成功回調中。這解決了我的瀏覽器無響應問題。

$.ajax({ 
    url: my_route_to_ask_if_photo_was_already_uploaded, 
    success: function (result) { 
    if (result == 'false') { 
     // ...other code 
     data.submit();   
    } 
    });