我想用AJAX上传文件。在过去,我通过使用神奇的jQuery表单插件实现了这一点,效果非常好。目前,我正在构建一个Rails应用程序,并尝试"Rails方式",所以我使用Form Helper和paperclip gem来添加文件附件。
rails文档警告Form Helper不适用于AJAX文件上传:
与其他表单不同,异步文件上传表单不是简单到为form_for提供remote:true。使用Ajax表单序列化是通过在浏览器内运行的JavaScript完成的由于JavaScript无法从硬盘读取文件,因此该文件无法上传。最常见的解决方法是使用不可见的作为表单提交目标的iframe。
很明显,没有现成的解决方案。所以我想知道最明智的做法是什么。似乎我有几个选择:
- 使用表单助手和iframe技巧
- 使用表单助手+加载jQuery表单插件来提交文件(不确定这是否能很好地处理Rails的真实性令牌等)
- 使用表单助手+回形针+[一些其他宝石]来扩展它的功能,以允许AJAX表单提交
三者似乎都有可能。我对#3知之甚少,特别是[其他宝石]部分。我发现了两个类似的问题(这个和这个),其中提到了Pic Upload的一个名为Uploadify的分支,但这两个问题都是2年前的问题,涉及Rails 2和3(Uploadify已经好几年没有更新了)。因此,考虑到变化有多大,我认为这真的是一个全新的问题:
在Rails4中使用AJAX上传文件的最佳方式是什么?
查看remotipart
宝石:https://github.com/JangoSteve/remotipart——只需很少的工作就可以把你一路带到那里!
使用@rails/ujs。
视图(.html.erb):
<%= file_field_tag :file, { id: "ajax_file_upload"} %>
控制器(_controller.rb):
def update
@record = YourModel.find(params[:id])
respond_to do |format|
if @record.update_attributes(params[:your_model])
format.json { render json: { success: true } }
else
error_messages = @record.errors.messages.values.flatten
format.json { render json: { success: false, errors: error_messages } }
end
end
end
javascript(.js)
const uploadFile = element => {
const formData = new FormData();
formData.append("your_model[attribute_name]", element.target.files[0]);
Rails.ajax({
url: "your_model/:id",
type: "PUT",
beforeSend(xhr, options) {
options.data = formData;
return true;
},
success: response => {
if (response.success) {
alert("File uploaded successfully");
}
else {
alert(response.errors.join("<br>"));
}
},
error: () => {
alert("ajax send error");
}
});
};
const documentOnReady = () => {
const fileField = document.getElementById("ajax_file_upload");
if (fileField) {
fileField.addEventListener("change", uploadFile);
}
}
document.addEventListener("turbolinks:load", documentOnReady);
注意:使用FormData时无需在ajax中设置RequestHeader。
FormData使用与如果编码类型设置为"0"则表单将使用的格式相同的格式;多部分/形式数据";
IMHO Rails在使用AJAX处理上传文件时并不完美,尤其是当您想要进度条时。我的建议是使用Javascript通过AJAX请求提交表单,就像您在(2)中建议的那样。如果你对Javascript感到满意,你就不会有太多问题。
我最近使用了同样的方法,使用了这个非常简单的JS库https://github.com/hayageek/jquery-upload-file我在这里写了更多的细节http://www.alfredo.motta.name/upload-video-files-with-rails-paperclip-and-jquery-upload-file/
对于一个带有上传带有标题和描述的电影的表单的应用程序,JS代码如下所示:
$(document).ready(function() {
var uploadObj = $("#movie_video").uploadFile({
url: "/movies",
multiple: false,
fileName: "movie[video]",
autoSubmit: false,
formData: {
"movie[title]": $('#movie_title').text(),
"movie[description]": $('#movie_description').text()
},
onSuccess:function(files,data,xhr)
{
window.location.href = data.to;
}
});
$("#fileUpload").click(function(e) {
e.preventDefault();
$.rails.disableFormElements($($.rails.formSubmitSelector));
uploadObj.startUpload();
});
});
远非完美,但在前端给你带来了灵活性。