0

Rails 3 アプリでRailscast #383 ( http://railscasts.com/episodes/383-uploading-to-amazon-s3 )の Jquery マルチファイル アップローダの修正版を使用していますが、微調整する必要があります。 S3 にファイルが既に存在するかどうかを確認し、存在する場合は再アップロードをスキップするようにします。

背景: ユーザーは大量のデータを更新する必要があります。たとえば、4MB のファイルを 500 個選択してアップロードするとします。必然的に、彼らのインターネット接続は切断され、どのファイルがアップロードされ、どのファイルがアップロードされていないかをユーザーが把握することを期待するのではなく、同じ 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
4

1 に答える 1

0

AJAX についてさらに学習した後 (2 回目のコメントで思いついた後)、AJAX 呼び出しを非同期にし、S3 POST コードを成功コールバック内に配置することが、実際に受け入れられる解決策のようです。これにより、ブラウザの非応答性の問題が解決されました。

$.ajax( {
  url: my_route_to_ask_if_photo_was_already_uploaded, 
  success: function (result) { 
    if (result == 'false') {
      // ...other code
      data.submit();          
    }
  });
于 2013-10-03T20:13:58.157 に答える