Rails直接使用aws-sdk gem和jQuery-File-Upload在heroku上进行S3上传 [英] Rails direct to S3 upload using aws-sdk gem and jQuery-File-Upload on heroku

查看:121
本文介绍了Rails直接使用aws-sdk gem和jQuery-File-Upload在heroku上进行S3上传的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图通过使用 jQuery-File-Upload aws-sdk gem ,以及 heroku的直接上传到S3的说明。这是在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>

这是相应的jQuery:

<$ p $($)$ $ $ $''$'$'$。$ 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'><< ; / b>)。append(progressBar);
fileInput.after(barContainer);
fileInput.fileupload({
fileInput:fileInput,
url:form.data 'url'),
类型:'POST',
autoUpload:true,
formData:form.data('form-data'),
paramName:'file', // S3不喜欢嵌套的名称字段,即name =user [avatar_url]
dataType:'XML',// S3如果成功则返回XML _action_status设置为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(正在加载...);
},
完成:function(e,data){
submitButton.prop('disabled',false);
progressBar.text(正在上传完成);
//提取键并从响应中生成URL
var key = $(data.jqXHR.responseXML).find(Key)。text();
var url ='//'+ form.data('host')+'/'+ key;
//创建隐藏字段
var input = $(< input />,{type:'hidden',name:fileInput.attr('name'),value:url})
form.append(input);
},
失败:function(e,data){
submitButton.prop('disabled',false);
progressBar。
css(背景,红色)。
text(失败);
}
});
});
});

尝试上传文件会产生这些日志:

 在2016-01-01 21:26:59 +0000开始POST/ users / bazley / update_pictures:: :: 1处理由CharactersController#update_pictures为HTML 
参数:{
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 \\r\\\
Content-Type:image / jpeg\r\\\
>
},
commit=>上传图片,
callsign=>bazley
}

表单提交成功,但不起作用,因为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

code>

在表单中,而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

表格:

 <%= 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 | %GT;
< div class =field>
<%= f.label:picture%>
<%= f.file_field:picture,accept:'image / jpeg,image / gif,image / png'%>
< / div>
<%= f.submitUpload pictures,class:btn btn-primary%>
<%end%>

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'])

编辑

控制台显示此错误:

 未捕获的TypeError:无法读取null的属性'innerHTML'

在这个文件里面(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函数(
tmpl.arg + ',tmpl',
var _e = tmpl.encode+ tmpl.helper +,_s =' +
str.replace(tmpl.regexp,tmpl.func)+
'; return _s;
);
返回数据? 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] +)%\})|(\ {%)|(%\})/克;
tmpl.func = function(s,p1,p2,p3,p4,p5){
if(p1){// HTML上下文中的空格,引号和退格符
return {
\\\
:\\\\

\r:\\r,
\t:\\t ,

} [p1] || \\+ p1; (p2){//插值:{%= prop%}或未转义:{%#prop%}
if(p2 ====){
} b $ b return'+ _e(+ p3 +)+';
}
return'+(+ p3 +== null?'':+ p3 +)+';
}
if(p4){//评估开始标记:{%
return';;
}
if(p5){//评估结束标记:%}
返回_s + =';
}
};
tmpl.encReg = / [<>''\x00] / g;
tmpl.encMap = {
<:& lt;,
>:& gt;,
&:& amp;,
\:& quot;,
':&#39;
};
tmpl.encode = function(s){
/ * jshint eqnull:true * /
return(s == null?:+ s).replace(
tmpl.encReg,
函数(c){
return tmpl.encMap [c] ||;
}
);
};
tmpl.arg =o;
tmpl.helper =,print = function(s,e){_ s + = e?(s == null?'':s):_ e(s);}+
,include =函数(S,d){_ S + = TMPL(S,d);};
if(typeof define ===function&& define.amd){
define(function(){
return tmpl;
));
} else {
$ .tmpl = tmpl;
}
}(this));


解决方案

最后找到答案here 。只需转到application.js并更改

  // = require jquery-fileupload 

  // =需要jquery -fileupload / basic 

基督串联。刚刚拿到50个重点就得到整个2多个意见。


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\"\r\nContent-Type: image/jpeg\r\n">
    }, 
    "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'\\])(?!(?:[^{]|\{(?!%))*%\})|(?:\{%(=|#)([\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 = {
        "<"   : "&lt;",
        ">"   : "&gt;",
        "&"   : "&amp;",
        "\""  : "&quot;",
        "'"   : "&#39;"
    };
    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.

这篇关于Rails直接使用aws-sdk gem和jQuery-File-Upload在heroku上进行S3上传的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆