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 %>