我只想通过REST API组织工作流程。我有一个允许上传图像的表单(enctype =“multipart / form-data”)。如何通过骨干处理此表单?请帮助我,如何使用文件字段将其序列化为JSON。
感谢。 维塔利
答案 0 :(得分:27)
如果您使用的是HTML5,则可以使用文件api中的readAsDataURL方法读取并将其存储在模型上。
这是我用来阅读和存储的代码。
var Image = Backbone.Model.extend({
readFile: function(file) {
var reader = new FileReader();
// closure to capture the file information.
reader.onload = (function(theFile,that) {
return function(e) {
//set model
that.set({filename: theFile.name, data: e.target.result});
};
})(file,this);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
});
答案 1 :(得分:3)
您可以尝试jquery.iframe.transport插件。
答案 2 :(得分:1)
恕我直言,您无法将文件序列化为JSON。 如果您需要将一些数据与文件一起发送,您可以使用POST方法将它们作为查询参数发送。
www.example.com/upload?param1=value1¶m2=value2
答案 3 :(得分:1)
通过AJAX提交文件没有好办法。所以我编写了一个伪造它的函数 - 它将一个秘密的iframe插入到你的DOM中,这个iframe永远不可见,但仍然可以作为提交表单的目标,并且它会为你的响应安装一个函数来调用文件来清理文件已上传。
让你上传表单的提交按钮激活我写的这个功能。它使用jQuery,因为它简单而且漂亮,但原则上不应该是绝对必要的:
function backgroundUpload(form, container) {
$(container).append('<iframe name="targetFrame" id="targetFrame" style="display: none; height: 0px; width:0px;" ></iframe>');
$(form).attr('target', 'targetFrame');
window.backgroundUploadComplete = function() {
//clear your form:
$(form).find(':file').val('');
$(form).find(':text').val('');
//do whatever you do to reload your screenful of data (I'm in Backbone.js, so:)
window.Docs.fetch().complete( function() { populateDocs(); });
//get rid of the target iframe
$('#targetFrame').remove();
};
$(form).submit();
}
然后让您的表单处理程序执行文件解析并保存返回字符串:
<script>window.parent.backgroundUploadComplete();</script>
您的表单可能如下所示:
<form id="uploadForm" method="POST" action="/your/form/processor/url" enctype="multipart/form-data">
<input type="file" name="file"/>
<!-- and other fields as needed -->
<input type="button" onClick="backgroundUpload(this.form, $('#documents'));" value="Upload" />
</form>
(#documents是这个表单所在的div。可能是任何DOM元素,它只需要一个家。)
答案 4 :(得分:1)
events : {
"click #uploadDocument" : "showUploadDocumentDetails",
"change #documents" : "documentsSelected",
"click .cancel-document" : "cancelDocument"
},
showUploadDocumentDetails : function(event) {
$('#id-gen-form').attr("enctype","multipart/form-data");
$('#id-gen-form').attr("action",this.model.url);
var config = {
support : "image/jpg,image/png,image/bmp,image/jpeg,image/gif", // Valid file formats
form: "id-gen-form", // Form ID
dragArea: "dragAndDropFiles", // Upload Area ID
uploadUrl: this.model.url // Server side upload url
};
initMultiUploader(config);
if($('#uploadDocument').attr("checked")){
$('#id-documentCategory-div').show();
$('#id-documentName-div').show();
this.model.set({"uploadDocument": "YES"},{silent: true});
}
else{
$('#id-documentCategory-div').hide();
$('#id-documentName-div').hide();
this.model.set({"uploadDocument": "NO"},{silent: true});
}
},
cancelDocument : function(event) {
var targ;
if (!event) event = window.event;
if (event.target) targ = event.target;
else if (event.srcElement) targ = event.srcElement;
$('#' + event.target.id).parent().parent().remove();
var documentDetails = this.model.get("documentDetails");
documentDetails = _.without(documentDetails, _(documentDetails).find(function(x) {return x.seqNum == event.target.id;}));
this.model.set({
"documentDetails" : documentDetails
}, {
silent : true
});
},
documentsSelected : function(event) {
/*var targ;
if (!event) event = window.event;
if (event.target) targ = event.target;
else if (event.srcElement) targ = event.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
var files = event.target.files; // FileList object
var html = [];
var documentDetails = [];
$(".files").html(html.join(''));
var _this = this;
_this.model.set({
"documentDetails" : documentDetails
}, {
silent : true
});
var seqNum = 0;
for(var i=0; i< files.length; i++){
(function(file) {
html.push("<tr class='template-upload' style='font-size: 10px;'>");
html.push("<td class='name'><span>"+file.name+"</span></td>");
html.push("<td class='size'><span>"+file.size+" KB <br/>"+file.type+"</span></td>");
//html.push("<td><div class='progress progress-success progress-striped active'style='width: 100px;' role='progressbar' aria-valuemin='0' aria-valuemax='100' aria-valuenow='0'><div class='bar' style='width:0%;'></div></div></td>");
if(LNS.MyesqNG.isMimeTypeSupported(file.type)){
if(!LNS.MyesqNG.isFileSizeExceeded(file.size)){
html.push("<td class='error' colspan='2'></td>");
var reader = new FileReader();
console.log(reader);
reader.onload = function(e) {
var targ;
if (!e) e = window.event;
if (e.target) targ = e.target;
else if (e.srcElement) targ = e.srcElement;
if (targ.nodeType == 3) // defeat Safari bug
targ = targ.parentNode;
console.log(e.target.result);
var content = e.target.result;
var document = new Object();
document.name = file.name;
document.type = file.type;
document.content = content;
document.seqNum = "document"+seqNum;
seqNum++;
documentDetails.push(document);
// _this.model.set({"documentDetails" : documentDetails},{silent:true});
};
reader.readAsDataURL(file, "UTF-8");
}else{
seqNum++;
html.push("<td class='error' colspan='2'><span class='label label-important'>Error</span> Too long</td>");
}
}else{
seqNum++;
html.push("<td class='error' colspan='2'><span class='label label-important'>Error</span> Not suported</td>");
}
html.push("<td><a id='document"+i+"' class='btn btn-warning btn-mini cancel-document'>Cancel</a></td>");
html.push("</tr>");
})(files[i]);
}
$(".files").html(html.join(''));*/
}
LNS.MyesqNG.isMimeTypeSupported = function(mimeType){
var mimeTypes = ['text/plain','application/zip','application/x-rar-compressed','application/pdf'];
if($.inArray(mimeType.toLowerCase(), mimeTypes) == -1) {
return false;
}else{
return true;
}
};
LNS.MyesqNG.isFileSizeExceeded = function(fileSize) {
var size = 2000000000000000000000000000;
if(Number(fileSize) > Number(size)){
return true;
}else{
return false;
}
};
Use this, it can work but not more than 5 MB file
答案 5 :(得分:0)
根据Anthony回答(https://stackoverflow.com/a/10916733/2750451),我根据推迟的对象在coffeescript中编写了一个解决方案。
readFile: (file) =>
def = $.Deferred()
reader = new FileReader()
reader.onload = (ev) =>
def.resolve
name: file.name
binary: ev.target.result
reader.onerror = ->
def.reject()
reader.readAsDataURL(file)
def.promise()
然后,你可以这样使用它
readFile(file)
.done (parsedFile) =>
# do whatever you want with parsedFile
@model.set
image_name: parsedFile.name
image: parsedFile.binary
@model.save
.fail ->
console.log "readFile has failed"
要在服务器端处理它(因为它是Base64编码的),这里是RoR中的解决方案(基于https://stackoverflow.com/a/16310953/2750451)
my_object.image = decode_image(params[:image])
my_object.image.name = params[:image_name]
def decode_image(encoded_file)
require 'base64'
image_data_string = split_base64(encoded_file)[:data]
Base64.decode64(image_data_string)
end
def split_base64(uri)
if uri.match(%r{^data:(.*?);(.*?),(.*)$})
return {
type: $1, # "image/png"
encoder: $2, # "base64"
data: $3, # data string
extension: $1.split('/')[1] # "png"
}
end
end
答案 6 :(得分:0)
对 Anthony Chua 的答案进行阐述。您需要像
一样向Backbone.Form.editors
添加图像处理
Backbone.Form.editors.Image = Backbone.Form.editors.Text.extend({
tagName: 'div',
events: {
'change input[type=file]': 'uploadFile',
'click .remove': 'removeFile'
},
initialize: function(options) {
_.bindAll(this, 'filepickerSuccess', 'filepickerError', 'filepickerProgress');
Backbone.Form.editors.Text.prototype.initialize.call(this, options);
this.$input = $('<input type="hidden" name="'+this.key+'" />');
this.$uploadInput = $('<input type="file" name="'+this.key+'" accept="image/*" />');
this.$loader = $('<p class="upload-status"><span class="loader"></span> please wait..</p>');
this.$error = $('<p class="upload-error error">Error</p>');
this.$list = $('<ul class="file-list">');
},
// return an array of file dicts
getValue: function() {
var val = this.$input.val();
return (val ? JSON.parse(val) : [])[0].value;
},
setValue: function(value) {
var str, files = value;
if (_(value).isObject()) {
str = JSON.stringify(value);
} else {
files = value ? JSON.parse(value) : [];
}
this.$input.val(str);
this.updateList(files);
},
render: function(options) {
Backbone.Form.editors.Text.prototype.render.apply(this, arguments);
this.$el.append(this.$input);
this.$el.append(this.$uploadInput);
this.$el.append(this.$loader.hide());
this.$el.append(this.$error.hide());
this.$el.append(this.$list);
return this;
},
uploadFile: function() {
var fileInput = this.$uploadInput.get(0);
var fileObj = fileInput.files[0]
var reader = new FileReader();
var that = this;
// closure to capture the file information.
reader.onload = function(file){
var dataURL = reader.result;
var fileValue = {
value: dataURL,
name: fileObj.name,
content_type: fileObj.type
}
that.filepickerSuccess(fileValue);
};
// Read in the image file as a data URL.
reader.readAsDataURL(fileObj);
},
filepickerSuccess: function(files) {
console.log('File (raw)', files);
this.$loader.hide();
this.$error.hide();
this.$uploadInput.val('');
// when uploading one file, it returns just an object
if (!_(files).isArray()) { files = [files]; }
// turn response array into a flatter array of objects
var newFiles = _(files).map(function(file, index) {
return {
url: "#",
value: file.value,
filename: file.name,
key: index,
content_type: file.type
};
});
console.log('File (processed)', newFiles);
this.setValue(newFiles);
},
filepickerError: function(msg) {
console.debug('File error', msg);
this.$loader.hide();
this.$error.show();
},
filepickerProgress: function(percent) {
this.$loader.show();
this.$error.hide();
},
updateList: function(files) {
// this code is currently duplicated as a handlebar helper (I wanted to let this
// backbone-forms field stand on its own)
this.$list.empty();
_(files).each(function(file) {
var a = $('<a>', {
target: '_blank',
href: file.url,
text: file.filename + ' (' + file.content_type + ') '
});
var li = $('<li>').append(a);
li.append(a, ' ', $('<a href="#" class="remove"><i class="icon-remove"></i></a>').data('key', file.key));
this.$list.append(li);
}, this);
this.$list[files.length ? 'show' : 'hide']();
},
removeFile: function(ev) {
if (ev) ev.preventDefault();
var files = this.getValue();
this.setValue([]);
}
});
您可以按以下方式使用上面的代码
var ImgSlot = Backbone.Model.extend({
defaults: {
},
schema: {
imageField: {
type: "Image"
}
}
})
使用以下格式呈现表单:
this.form = new Backbone.Form({
model: new ImgSlot(),
submitButton: "Example Image file input handling"
}).render();
var errors = that.form.commit({validate: true})
if(errors != null)
{
return false;
}
var data = that.form.model.attributes;
console.debug(data.imageField); // Will return base64 of image selected.
答案 7 :(得分:-14)
在HTML5(包括IE9)之前无法通过AJAX提交文件。
您需要通过ajax同步模型属性,然后使用该文件发送另一个html表单帖子,然后以某种方式同步它们。通常,将模型保存在ajax上,获取id,将id添加到其他表单,然后发布文件。
“jquery.form”中的jQuery插件可以帮助您构建一个表单来发布文件。它管理“隐藏的iframe技巧”,使其看起来像最终用户的AJAX。
你可能只需花一些时间在谷歌上搜索“隐藏的iframe技巧”......