在 heroku 上使用 aws-sdk gem 和 jQuery-File-Upload 将 Rails 直接上传到 S3 [英] Rails direct to S3 upload using aws-sdk gem and jQuery-File-Upload on heroku
问题描述
我正在尝试使用 jQuery-File-Upload 和aws-sdk gem,并关注 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 = $("<div class='bar'></div>");var barContainer = $("<div class='progress'></div>").append(progressBar);fileInput.after(barContainer);文件输入.文件上传({文件输入:文件输入,url: form.data('url'),类型:'POST',自动上传:真实,formData: form.data('form-data'),paramName: 'file',//S3 不喜欢嵌套名称字段,即 name="user[avatar_url]"dataType: 'XML',//如果 success_action_status 设置为 201,S3 返回 XML替换文件输入:假,进度:功能(e,数据){var progress = parseInt(data.loaded/data.total * 100, 10);progressBar.css('width', progress + '%')},开始:功能(e){submitButton.prop('禁用', true);进度条.css('背景','绿色').css('显示','块').css('宽度','0%').text("加载中...");},完成:功能(e,数据){submitButton.prop('禁用', false);progressBar.text("上传完成");//从响应中提取密钥并生成 URLvar key = $(data.jqXHR.responseXML).find("Key").text();var url = '//' + form.data('host') + '/' + key;//创建隐藏域var input = $("<input/>", { type:'hidden', name: fileInput.attr('name'), value: url })形式.追加(输入);},失败:函数(e,数据){submitButton.prop('禁用', false);进度条.css(背景",红色").文本(失败");}});});});
尝试上传文件会产生以下日志:
开始 POST "/users/bazley/update_pictures" for ::1 at 2016-01-01 21:26:59 +0000 由 CharactersController#update_pictures 处理为 HTML参数: {"utf8"=>"✓","authenticity_token"=>"rvhu...fhdg==",标准图片"=>{"图片"=>#<ActionDispatch::Http::UploadedFile:0x0000010b32f530@tempfile=#<Tempfile:/var/folders/19/_vdcl1r913g6fzvk1l56x4km0000gn/T/RackMultipart20160101-49946-7t94p.jpg>,@original_filename="europe.jpg",@content_type="图像/jpeg",@headers="Content-Disposition: form-data; name="standardpicture[picture]"; filename="europe.jpg"
Content-Type: image/jpeg
">},"commit"=>"上传图片",呼号"=>巴兹利"}
表单提交成功,但它不起作用,因为Rails没有在S3上保存正确的位置(图片",一个字符串);相反,它认为位置是
"picture"=>#<ActionDispatch::Http::UploadedFile:0x0000010b32f530
您可以在提交的参数中看到这一点.它应该是这样的:
"picture"=>"//websmash.s3.amazonaws.com/uploads/220f5378-1e0f-4823-9527-3d1170089a49/europe.jpg"}, "commit"=>"上传图片"}
我不明白的是,为什么当表单中似乎存在所有正确信息时,参数会出错.上面写得很清楚
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')结尾
形式:
<%= form_for :standardpicture, url: update_pictures_user_path,html: { id: "pic-upload", class: "directUpload",数据:{'表单数据'=>(@s3_direct_post.fields),'网址' =>@s3_direct_post.url,'主机' =>URI.parse(@s3_direct_post.url).host }}做|f|%><div class="field"><%= f.label :picture %><%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
<%= f.submit "上传图片", class: "btn btn-primary" %><%结束%>
aws.rb 初始值设定项:
aws.config.update({地区:'us-east-1',凭证:Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']),})S3_BUCKET = Aws::S3::Resource.new.bucket(ENV['S3_BUCKET'])
编辑
控制台显示此错误:
未捕获的类型错误:无法读取 null 的属性innerHTML"
在这个文件中(tmpl.self-c210...9488.js?body=1):
(函数 ($) {严格使用";var tmpl = 函数(字符串,数据){var f = !/[^w-.:]/.test(str) ?tmpl.cache[str] = tmpl.cache[str] ||tmpl(tmpl.load(str)) :新功能(tmpl.arg + ',tmpl',"var _e=tmpl.encode" + tmpl.helper + ",_s='" +str.replace(tmpl.regexp, tmpl.func) +"';返回_s;");返回数据 ?f(数据,tmpl):函数(数据){返回 f(data, tmpl);};};tmpl.cache = {};tmpl.load = 函数 (id) {返回 document.getElementById(id).innerHTML;};tmpl.regexp =/([s'\])(?!(?:[^{]|{(?!%))*%})|(?:{%(=|#)([sS]+?)%})|({%)|(%})/g;tmpl.func = 函数 (s, p1, p2, p3, p4, p5) {if (p1) {//HTML 上下文中的空格、引号和退格返回 {"
": "\n","
": "\r"," ": "\t"," " : " "}[p1] ||"\" + p1;}if (p2) {//插值:{%=prop%},或未转义:{%#prop%}如果(p2 ===="){return "'+_e(" + p3 + ")+'";}return "'+(" + p3 + "==null?'':" + p3 + ")+'";}if (p4) {//评估开始标签:{%返回 "';";}if (p5) {//评估结束标记:%}返回_s+=";}};tmpl.encReg =/[<>&"'x00]/g;tmpl.encMap = {<": "<",>": ">",&": "&",""" : ""","'": "'"};tmpl.encode = 函数 (s) {/*jshint eqnull:true */return (s == null ? "" : "" + s).replace(tmpl.encReg,功能(c){返回 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 (typeofdefine === "function" &&define.amd) {定义(函数(){返回 tmpl;});} 别的 {$.tmpl = tmpl;}}(这));
终于找到答案了此处.只需转到 application.js 并更改
//= 需要 jquery-fileupload
到
//= 需要 jquery-fileupload/basic
基督串联.仅仅因为获得了整整 2 次观看而激怒了 50 个代表点.
I'm trying to achieve direct to Amazon S3 upload in Rails using jQuery-File-Upload and the aws-sdk gem, and following heroku's direct to S3 upload instructions. This is the upload form produced in the html:
<form id="pic-upload"
class="directUpload"
data-form-data="{
"key":"uploads/59c99e44-6bf2-4937-9680-02c839244b33/${filename}",
"success_action_status":"201",
"acl":"public-read",
"policy":"eyJle...In1dfQ==",
"x-amz-credential":"AKIAJCOB5HQVW5IUPYGQ/20160101/us-east-1/s3/aws4_request",
"x-amz-algorithm":"AWS4-HMAC-SHA256",
"x-amz-date":"20160101T010335Z",
"x-amz-signature":"0f32ae...238e"}"
data-url="https://websmash.s3.amazonaws.com"
data-host="websmash.s3.amazonaws.com"
enctype="multipart/form-data"
action="/users/bazley/update_pictures"
accept-charset="UTF-8"
method="post">
This is the corresponding 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 = $("<div class='bar'></div>");
var barContainer = $("<div class='progress'></div>").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 i.e. 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 = $("<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");
}
});
});
});
Trying to upload a file produces these logs:
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=#<Tempfile:/var/folders/19/_vdcl1r913g6fzvk1l56x4km0000gn/T/RackMultipart20160101-49946-7t94p.jpg>,
@original_filename="europe.jpg",
@content_type="image/jpeg",
@headers="Content-Disposition: form-data; name="standardpicture[picture]"; filename="europe.jpg"
Content-Type: image/jpeg
">
},
"commit"=>"Upload pictures",
"callsign"=>"bazley"
}
The form submits successfully, but it isn't working because Rails doesn't save the correct location ("picture", a string) on S3; instead it thinks the location is
"picture"=>#<ActionDispatch::Http::UploadedFile:0x0000010b32f530
You can see this in the submitted parameters. It should be something like:
"picture"=>"//websmash.s3.amazonaws.com/uploads/220f5378-1e0f-4823-9527-3d1170089a49/europe.jpg"}, "commit"=>"Upload pictures"}
What I don't understand is why it's getting the parameters wrong when all the correct information seems to be present in the form. It clearly says
data-url="https://websmash.s3.amazonaws.com"
in the form, and the jQuery includes
url: form.data('url'),
so what's going wrong?
For completeness: in the controller:
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
The form:
<%= form_for :standardpicture, url: update_pictures_user_path,
html: { id: "pic-upload", class: "directUpload",
data: { 'form-data' => (@s3_direct_post.fields),
'url' => @s3_direct_post.url,
'host' => URI.parse(@s3_direct_post.url).host }
} do |f| %>
<div class="field">
<%= f.label :picture %>
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
</div>
<%= f.submit "Upload pictures", class: "btn btn-primary" %>
<% end %>
aws.rb initializer:
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'])
EDIT
The console shows this error:
Uncaught TypeError: Cannot read property 'innerHTML' of null
inside this file (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'\])(?!(?:[^{]|{(?!%))*%})|(?:{%(=|#)([sS]+?)%})|({%)|(%})/g;
tmpl.func = function (s, p1, p2, p3, p4, p5) {
if (p1) { // whitespace, quote and backspace in HTML context
return {
"
": "\n",
"
": "\r",
" ": "\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));
Finally found the answer here. Simply had to go to application.js and change
//= require jquery-fileupload
to
//= require jquery-fileupload/basic
Christ on a tandem. Just pissed away 50 rep points on getting a whole 2 more views.
这篇关于在 heroku 上使用 aws-sdk gem 和 jQuery-File-Upload 将 Rails 直接上传到 S3的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!