Ответ 1
Попробуйте новый Gem, называемый CarrierWaveDirect, он позволяет загружать файлы напрямую на S3 с использованием формы html и легко перемещать обработку изображений в фоновый процесс
Я хочу добавить функциональность в приложение Rails для загрузки файлов непосредственно на Amazon S3. По моим исследованиям, общий консенсус, похоже, заключается в использовании s3-swf-upload-plugin. Я установил пример приложения, используя этот драгоценный камень, но я не могу заставить его играть хорошо, только разрешая выбор одного файла. Я также хотел бы создать запись с записью и использовать paperclip для создания эскиза, для которого я могу найти небольшое руководство.
Итак, мои вопросы:
(1) Я на правильном пути, используя этот драгоценный камень, или я должен брать другую оценку?
(2) есть ли там какие-то образцы, которые я мог бы использовать для справки?
Любая помощь будет высоко оценена.
Крис
Попробуйте новый Gem, называемый CarrierWaveDirect, он позволяет загружать файлы напрямую на S3 с использованием формы html и легко перемещать обработку изображений в фоновый процесс
Не уверен, можете ли вы легко изменить его, чтобы загружать только один файл за раз, но этот драгоценный камень отлично работает для меня. Он основан на одном из Rails Bates Railscast:
Попробуйте посмотреть на несущую волну https://github.com/jnicklas/carrierwave (поддерживает s3) Загрузка нескольких файлов с помощью несущей и добавление http://blog.assimov.net/post/4306595758/multi-file-upload-with-uploadify-and-carrierwave-on
Если вы используете Rails 3, просмотрите мои проекты:
Пример проекта с использованием Rails 3, Flash и MooTools на основе FancyUploader для загрузки непосредственно на S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-FancyUploader
Пример проекта с использованием Rails 3, Flash/Silverlight/GoogleGears/BrowserPlus и на основе JQuery для загрузки непосредственно на S3: https://github.com/iwasrobbed/Rails3-S3-Uploader-Plupload
Кстати, вы можете делать пост-обработку с помощью Paperclip, используя что-то вроде этого сообщения в блоге:
http://www.railstoolkit.com/posts/fancyupload-amazon-s3-uploader-with-paperclip
Я адаптировал Heroku прямо к S3-загружающему решению в Rails (который использует jQuery-File-Upload и aws-sdk gem), поэтому загрузка на S3 может выполняться удаленно с помощью ajax. Надеюсь, это полезно:
posts_controller.rb
before_action :set_s3_direct_post, only: [:index, :create]
before_action :delete_picture_from_s3, only: [:destroy]
class PostsController < ApplicationController
def index
.
.
end
def create
@post = @user.posts.build(post_params)
if @post.save
format.html
format.js
end
end
def destroy
Post.find(params[:id]).destroy
end
private
def set_s3_direct_post
return S3_BUCKET.presigned_post(key: "uploads/#{SecureRandom.uuid}/${filename}", success_action_status: '201', acl: 'public-read')
end
def delete_picture_from_s3
key = params[:picture_url].split('amazonaws.com/')[1]
S3_BUCKET.object(key).delete
return true
rescue => e
# If anyone knows a good way to deal with a defunct file sitting in the bucket, please speak up.
return true
end
def post_params
params.require(:post).permit(:content, :picture_url)
end
end
posts.html.erb
<div class="info" data-url="<%= @s3_direct_post.url %>"
data-formdata="<%= (@s3_direct_post.fields.to_json) %>"
data-host="<%= URI.parse(@s3_direct_post.url).host %>">
</div>
Форма
<%= form_for(:post, url: :posts, method: :post,
html: { class: "post_form", id: "post_form-#{post.id}" }
) do |f| %>
<%= f.text_area :content, id: "postfield-#{post.id}", class: "postText" %>
<%= f.button( :submit, name: "Post", title: "Post" ) do %>
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span>
<% end %>
<span class="postuploadbutton" id="postUp-<%= post.id %>" title="Add file" >
<span class="glyphicon glyphicon-upload" aria-hidden="true"></span>
</span>
<span title="Cancel file" class="noticecancelupload" id="postCancel-<%= post.id %>" >
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
</span>
<%= f.file_field :picture_url, accept: 'image/jpeg,image/gif,image/png',
class: "notice_file_field", id: "postFile-#{post.id}" %>
<% end %>
_post.html.erb
<%= button_to post_path(
params: {
id: post.id,
picture_url: post.picture_url
}
),
class: 'btn btn-default btn-xs blurme',
data: { confirm: "Delete post: are you sure?" },
method: :delete do %>
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
<% end %>
Javascript в каждом _post.html.erb
$(document).off('click',"#postUp-<%= post.id %>");
$(document).on('click', '#postUp-<%= post.id %>', function(e) {
prepareUpload("#post_form-<%= post.id %>");
$('#postFile-<%= post.id %>').trigger("click");
});
$(document).off('click',"#postCancel-<%= post.id %>");
$(document).on('click', '#postCancel-<%= post.id %>', function(e) {
$(".appendedInput").remove(); // $('#postFile-<% post.id %>').val(""); doesn't work for me
$('.progBar').css('background','white').text("");
});
$(document).off('submit',"#post_form-<%= post.id %>"); // without this the form submitted multiple times in production
$(document).on('submit', '#post_form-<%= post.id %>', function(e) { // don't use $('#post_form-<%= post.id %>').submit(function() { so it doesn't bind to the #post_form (so it still works after ajax loading)
e.preventDefault(); // prevent normal form submission
if ( validatePostForm('<%= post.id %>') ) {
$.ajax({
type: 'POST',
url: $(this).attr('action'),
data: $(this).serialize(),
dataType: 'script'
});
$('#postCancel-<%= post.id %>').trigger("click");
}
});
function validatePostForm(postid) {
if ( jQuery.isBlank($('#postfield-' + postid).val()) && jQuery.isBlank($('#postFile-' + postid).val()) ) {
alert("Write something fascinating or add a picture.");
return false;
} else {
return true;
}
}
Javascript в application.js
function prepareUpload(feckid) {
$(feckid).find("input:file").each(function(i, elem) {
var fileInput = $(elem);
var progressBar = $("<div class='progBar'></div>");
var barContainer = $("<div class='progress'></div>").append(progressBar);
fileInput.after(barContainer);
var maxFS = 10 * 1024 * 1024;
var info = $(".info");
var urlnumbnuts = info.attr("data-url");
var formdatanumbnuts = jQuery.parseJSON(info.attr("data-formdata"));
var hostnumbnuts = info.attr("data-host");
var form = $(fileInput.parents('form:first'));
fileInput.fileupload({
fileInput: fileInput,
maxFileSize: maxFS,
url: urlnumbnuts,
type: 'POST',
autoUpload: true,
formData: formdatanumbnuts,
paramName: 'file',
dataType: 'XML',
replaceFileInput: false,
add: function (e, data) {
$.each(data.files, function (index, file) {
if (file.size > maxFS) {
alert('Alas, the file exceeds the maximum file size of 10MB.');
form[0].reset();
return false;
} else {
data.submit();
return true;
}
});
},
progressall: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
progressBar.css('width', progress + '%')
},
start: function (e) {
progressBar.
css('background', 'orange').
css('display', 'block').
css('width', '0%').
text("Preparing...");
},
done: function(e, data) {
var key = $(data.jqXHR.responseXML).find("Key").text();
var url = '//' + hostnumbnuts + '/' + key;
var input = $('<input />', { type:'hidden', class:'appendedInput',
name: fileInput.attr('name'), value: url });
form.append(input);
progressBar.
css('background', 'green').
text("Ready");
},
fail: function(e, data) {
progressBar.
css("background", "red").
css("color", "black").
text("Failed");
}
});
});
} // function prepareUpload()
create.js.erb
$(".info").attr("data-formdata", '<%=raw @s3_direct_post.fields.to_json %>'); // don't use .data() to set attributes
$(".info").attr("data-url", "<%= @s3_direct_post.url %>");
$(".info").attr("data-host", "<%= URI.parse(@s3_direct_post.url).host %>");
$('.post_form')[0].reset();
$('.postText').val('');
application.js
//= require jquery-fileupload/basic
конфигурации/инициализаторы/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'])
Примечания:
Это решение предназначено для нескольких почтовых форм на странице index.html.erb. Вот почему информация @s3_direct_post
помещается внутри div класса info
внутри index.html.erb, а не в каждую форму сообщения. Это означает, что на странице отображается только один @s3_direct_post
, независимо от количества форм на странице. Данные, находящиеся внутри @s3_direct_post
, только захватываются (с вызовом prepareUpload()
) при нажатии кнопки загрузки файла. После подачи в диспетчере сообщений создается новый @s3_direct_post
, а информация внутри .info
обновляется с помощью create.js.erb. Сохранение данных @s3_direct_post
внутри формы означает, что сразу могут существовать разные экземпляры @s3_direct_post
, что приводит к ошибкам с генерацией имени файла.
Вам нужно :set_s3_direct_post
как в действии индекса контроллера сообщений (готово к первой загрузке), так и в действии create (готово для второй и последующей загрузки).
Подача обычной формы предотвращается e.preventDefault();
, поэтому ее можно сделать "вручную" с помощью $.ajax({
. Почему бы просто не использовать remote: true
в форме? Поскольку в Rails загрузка файла выполняется с помощью HTML-запроса и обновления страницы, даже когда вы пытаетесь сделать это удаленно.
Используйте info.attr()
, а не info.data()
, чтобы установить и получить атрибуты @s3_direct_post
, потому что info.data не обновляется
(например, см. этот вопрос). Это означает, что вам также нужно вручную проанализировать атрибут в объекте, используя jQuery.parseJSON()
(который .data() фактически делает автоматически).
Не используйте //= require jquery-fileupload
в application.js. Эта ошибка была реальной баллахой для идентификации (см. здесь). оригинальное решение Heroku не работал, пока я не изменил это.
Вы можете использовать Paperclip для загрузки на S3 (см. документация) и создавать эскизы, хотя сначала он загружается во временную папку, после чего обработка изображения может быть применена перед загрузкой файла на S3.
Что касается примеров такой конфигурации, их много в блогосфере и в StackOverflow, например. this.