Rails使用aws-sdk gem和heroku上的jQuery-File-Upload直接进入S3上传

我正在尝试使用jQuery-File-Upload和aws-sdk gem直接在Rails中实现Amazon S3上传,并遵循heroku直接到S3上传说明。 这是在html中生成的上传表单:

这是相应的jQuery:

 $(function() { $('.directUpload').find("input:file").each(function(i, elem) { var fileInput = $(elem); var form = $(fileInput.parents('form:first')); var submitButton = form.find('input[type="submit"]'); var progressBar = $("
"); var barContainer = $("
").append(progressBar); fileInput.after(barContainer); fileInput.fileupload({ fileInput: fileInput, url: form.data('url'), type: 'POST', autoUpload: true, formData: form.data('form-data'), paramName: 'file', // S3 does not like nested name fields ie name="user[avatar_url]" dataType: 'XML', // S3 returns XML if success_action_status is set to 201 replaceFileInput: false, progressall: function (e, data) { var progress = parseInt(data.loaded / data.total * 100, 10); progressBar.css('width', progress + '%') }, start: function (e) { submitButton.prop('disabled', true); progressBar. css('background', 'green'). css('display', 'block'). css('width', '0%'). text("Loading..."); }, done: function(e, data) { submitButton.prop('disabled', false); progressBar.text("Uploading done"); // extract key and generate URL from response var key = $(data.jqXHR.responseXML).find("Key").text(); var url = '//' + form.data('host') + '/' + key; // create hidden field var input = $("", { type:'hidden', name: fileInput.attr('name'), value: url }) form.append(input); }, fail: function(e, data) { submitButton.prop('disabled', false); progressBar. css("background", "red"). text("Failed"); } }); }); });

尝试上传文件会生成以下日志:

 Started POST "/users/bazley/update_pictures" for ::1 at 2016-01-01 21:26:59 +0000 Processing by CharactersController#update_pictures as HTML Parameters: { "utf8"=>"✓", "authenticity_token"=>"rvhu...fhdg==", "standardpicture"=>{ "picture"=>#<ActionDispatch::Http::UploadedFile:0x0000010b32f530 @tempfile=#, @original_filename="europe.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"standardpicture[picture]\"; filename=\"europe.jpg\"\r\nContent-Type: image/jpeg\r\n"> }, "commit"=>"Upload pictures", "callsign"=>"bazley" } 

表单成功提交,但由于Rails没有在S3上保存正确的位置(“picture”,一个字符串),因此无法正常工作; 相反,它认为位置是

 "picture"=>#<ActionDispatch::Http::UploadedFile:0x0000010b32f530 

您可以在提交的参数中看到这一点。 它应该是这样的:

 "picture"=>"//websmash.s3.amazonaws.com/uploads/220f5378-1e0f-4823-9527-3d1170089a49/europe.jpg"}, "commit"=>"Upload pictures"} 

我不明白的是,当表格中出现所有正确的信息时,为什么它会导致参数错误。 它清楚地说

 data-url="https://websmash.s3.amazonaws.com" 

在表单中,jQuery包含

 url: form.data('url'), 

出了什么问题?

为了完整性:在控制器中:

 before_action :set_s3_direct_post . . def set_s3_direct_post @s3_direct_post = S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read') end 

表格:

  (@s3_direct_post.fields), 'url' => @s3_direct_post.url, 'host' => URI.parse(@s3_direct_post.url).host } } do |f| %> 

aws.rb初始化器:

 Aws.config.update({ region: 'us-east-1', credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']), }) S3_BUCKET = Aws::S3::Resource.new.bucket(ENV['S3_BUCKET']) 

编辑

控制台显示此错误:

 Uncaught TypeError: Cannot read property 'innerHTML' of null 

在这个文件里面(tmpl.self-c210 … 9488.js?body = 1):

 (function ($) { "use strict"; var tmpl = function (str, data) { var f = !/[^\w\-\.:]/.test(str) ? tmpl.cache[str] = tmpl.cache[str] || tmpl(tmpl.load(str)) : new Function( tmpl.arg + ',tmpl', "var _e=tmpl.encode" + tmpl.helper + ",_s='" + str.replace(tmpl.regexp, tmpl.func) + "';return _s;" ); return data ? f(data, tmpl) : function (data) { return f(data, tmpl); }; }; tmpl.cache = {}; tmpl.load = function (id) { return document.getElementById(id).innerHTML; }; tmpl.regexp = /([\s'\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\s\S]+?)%\})|(\{%)|(%\})/g; tmpl.func = function (s, p1, p2, p3, p4, p5) { if (p1) { // whitespace, quote and backspace in HTML context return { "\n": "\\n", "\r": "\\r", "\t": "\\t", " " : " " }[p1] || "\\" + p1; } if (p2) { // interpolation: {%=prop%}, or unescaped: {%#prop%} if (p2 === "=") { return "'+_e(" + p3 + ")+'"; } return "'+(" + p3 + "==null?'':" + p3 + ")+'"; } if (p4) { // evaluation start tag: {% return "';"; } if (p5) { // evaluation end tag: %} return "_s+='"; } }; tmpl.encReg = /[&"'\x00]/g; tmpl.encMap = { "" : ">", "&" : "&", "\"" : """, "'" : "'" }; tmpl.encode = function (s) { /*jshint eqnull:true */ return (s == null ? "" : "" + s).replace( tmpl.encReg, function (c) { return tmpl.encMap[c] || ""; } ); }; tmpl.arg = "o"; tmpl.helper = ",print=function(s,e){_s+=e?(s==null?'':s):_e(s);}" + ",include=function(s,d){_s+=tmpl(s,d);}"; if (typeof define === "function" && define.amd) { define(function () { return tmpl; }); } else { $.tmpl = tmpl; } }(this)); 

终于在这里找到了答案。 只需要转到application.js并进行更改

 //= require jquery-fileupload 

 //= require jquery-fileupload/basic 

基督在串联。 只得到了50个代表就获得了整整2个视图。

此代码使用aws-sdk-v2作为ruby,它已被提交给Heroku以更新他们关于s3直接上传的文档( https://devcenter.heroku.com/articles/direct-to-s3-image-uploads-in-rails #pre-signed-post )

这方面的一些方面是没有必要的,即在上传到s3后将您自己的照片对象存储在数据库中。 代码显然不是那么好,这只是为了让它工作。 我很高兴听到任何改进它的建议,或者知道这里有什么东西不适合你。

Sidekiq作业有一个方法do_work,这是我在BaseJob中定义的类,为我提供了一些额外的指标和信息,如果没有SidekiqPro你就无法获得,所以如果你不做任何事情,你需要将其改为“执行”特别与Sidekiq表演课程。

 #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| %>  <%= 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 %>  <% end %>