Javascript 通过AJAX和jQuery使用HTML5文件上传

Javascript 通过AJAX和jQuery使用HTML5文件上传,javascript,jquery,ajax,html,file-upload,Javascript,Jquery,Ajax,Html,File Upload,诚然,在堆栈溢出上也存在类似的问题,但似乎没有一个完全符合我的要求 以下是我想要做的: 上传完整形式的数据,其中一部分是单个文件 使用Codeigniter的文件上载库 到现在为止,一切都很好。数据在我需要时进入我的数据库。但我也希望通过AJAX帖子提交我的表格: 使用本机HTML5文件API,而不是flash或iframe解决方案 最好与低级的.ajax()jQuery方法接口 我想我可以想象如何使用纯javascript在字段值改变时自动上传文件,但我更愿意在jQuery中提交时一次

诚然,在堆栈溢出上也存在类似的问题,但似乎没有一个完全符合我的要求

以下是我想要做的:

  • 上传完整形式的数据,其中一部分是单个文件
  • 使用Codeigniter的文件上载库
到现在为止,一切都很好。数据在我需要时进入我的数据库。但我也希望通过AJAX帖子提交我的表格:

  • 使用本机HTML5文件API,而不是flash或iframe解决方案
  • 最好与低级的
    .ajax()
    jQuery方法接口
我想我可以想象如何使用纯javascript在字段值改变时自动上传文件,但我更愿意在jQuery中提交时一次性完成。我认为通过查询字符串是不可能的,因为我需要传递整个file对象,但是我现在有点不知道该怎么做


这能实现吗?

这并不难。首先,看一看

因此,在提交表单时,捕获提交过程并

var file = document.getElementById('fileBox').files[0]; //Files[0] = 1st file
var reader = new FileReader();
reader.readAsText(file, 'UTF-8');
reader.onload = shipOff;
//reader.onloadstart = ...
//reader.onprogress = ... <-- Allows you to update a progress bar.
//reader.onabort = ...
//reader.onerror = ...
//reader.onloadend = ...


function shipOff(event) {
    var result = event.target.result;
    var fileName = document.getElementById('fileBox').files[0].name; //Should be 'picture.jpg'
    $.post('/myscript.php', { data: result, name: fileName }, continueSubmission);
}
或者类似的。我可能错了(如果我错了,请纠正我),但这应该将文件存储为服务器上
/uploads/
中的
1287916771myPicture.jpg
,并使用包含服务器上文件名的JSON变量(对
continueSubmission()
函数)进行响应

退房并离开

在上面的页面上,它详细介绍了如何使用,以及如何满足您的其他需求(例如图像、视频等)。

使用jQuery(不使用FormData API),您可以使用以下内容:

function readFile(file){
   var loader = new FileReader();
   var def = $.Deferred(), promise = def.promise();

   //--- provide classic deferred interface
   loader.onload = function (e) { def.resolve(e.target.result); };
   loader.onprogress = loader.onloadstart = function (e) { def.notify(e); };
   loader.onerror = loader.onabort = function (e) { def.reject(e); };
   promise.abort = function () { return loader.abort.apply(loader, arguments); };

   loader.readAsBinaryString(file);

   return promise;
}

function upload(url, data){
    var def = $.Deferred(), promise = def.promise();
    var mul = buildMultipart(data);
    var req = $.ajax({
        url: url,
        data: mul.data,
        processData: false,
        type: "post",
        async: true,
        contentType: "multipart/form-data; boundary="+mul.bound,
        xhr: function() {
            var xhr = jQuery.ajaxSettings.xhr();
            if (xhr.upload) {

                xhr.upload.addEventListener('progress', function(event) {
                    var percent = 0;
                    var position = event.loaded || event.position; /*event.position is deprecated*/
                    var total = event.total;
                    if (event.lengthComputable) {
                        percent = Math.ceil(position / total * 100);
                        def.notify(percent);
                    }                    
                }, false);
            }
            return xhr;
        }
    });
    req.done(function(){ def.resolve.apply(def, arguments); })
       .fail(function(){ def.reject.apply(def, arguments); });

    promise.abort = function(){ return req.abort.apply(req, arguments); }

    return promise;
}

var buildMultipart = function(data){
    var key, crunks = [], bound = false;
    while (!bound) {
        bound = $.md5 ? $.md5(new Date().valueOf()) : (new Date().valueOf());
        for (key in data) if (~data[key].indexOf(bound)) { bound = false; continue; }
    }

    for (var key = 0, l = data.length; key < l; key++){
        if (typeof(data[key].value) !== "string") {
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"; filename=\""+data[key].value[1]+"\"\r\n"+
                "Content-Type: application/octet-stream\r\n"+
                "Content-Transfer-Encoding: binary\r\n\r\n"+
                data[key].value[0]);
        }else{
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"\r\n\r\n"+
                data[key].value);
        }
    }

    return {
        bound: bound,
        data: crunks.join("\r\n")+"\r\n--"+bound+"--"
    };
};

//----------
//---------- On submit form:
var form = $("form");
var $file = form.find("#file");
readFile($file[0].files[0]).done(function(fileData){
   var formData = form.find(":input:not('#file')").serializeArray();
   formData.file = [fileData, $file[0].files[0].name];
   upload(form.attr("action"), formData).done(function(){ alert("successfully uploaded!"); });
});
函数readFile(文件){
var loader=new FileReader();
var def=$.Deferred(),promise=def.promise();
//---提供经典的延迟接口
loader.onload=函数(e){def.resolve(e.target.result);};
loader.onprogress=loader.onloadstart=function(e){def.notify(e);};
loader.onerror=loader.onabort=function(e){def.reject(e);};
promise.abort=函数(){return loader.abort.apply(loader,arguments);};
loader.readAsBinaryString(文件);
回报承诺;
}
功能上传(url、数据){
var def=$.Deferred(),promise=def.promise();
var mul=buildMultipart(数据);
var req=$.ajax({
url:url,
数据:mul.data,
processData:false,
类型:“post”,
async:true,
contentType:“多部分/表单数据;边界=“+mul.bound,
xhr:function(){
var xhr=jQuery.ajaxSettings.xhr();
if(xhr.upload){
xhr.upload.addEventListener('progress',函数(事件){
风险值百分比=0;
var position=event.loaded | | event.position;/*event.position已弃用*/
var total=事件总数;
if(事件长度可计算){
百分比=数学单元(位置/总数*100);
def.通知(百分比);
}                    
},假);
}
返回xhr;
}
});
完成(函数(){def.resolve.apply(def,参数);})
.fail(函数(){def.reject.apply(def,参数);});
promise.abort=function(){return req.abort.apply(req,arguments);}
回报承诺;
}
var buildMultipart=函数(数据){
var键,crunks=[],bound=false;
当(!绑定){
绑定=$.md5?$.md5(新日期().valueOf()):(新日期().valueOf());
如果(~data[key].indexOf(bound)){bound=false;continue;}
}
for(var key=0,l=data.length;key

使用FormData API,您只需将表单的所有字段添加到FormData对象并通过$.ajax发送即可({url:url,data:FormData,processData:false,contentType:false,type:“POST”})

我不知道Codeigniter部分,但对于jQuery部分,请查看相关信息:嘿,Clark,我理解正确吗?这将在上传的文件从文件系统加载到FileReader构造函数后立即发送该文件,绕过jQuery的低级.ajax处理程序。那么表单的其余部分将正常提交?好吧,所以我以前的理解是错误的。现在,我获取一个图像的readAsDataUrl,将其添加到.ajax中的数据字符串中,并一起提交我的所有信息。我以前的解决方案涉及CodeIgniter的默认文件输入类,该类从$_FILES['field']中获取数据,因此看起来我需要切换到其他解决方案来解析base64图像数据。欢迎对此提出任何建议,在这里更新您的答案,一旦我完成实现,我会将其标记为正确。@Joshua Cody-我更新了答案,提供了更多细节。你们必须原谅我在很多卫星上并没有使用CodeIgniter,也不能告诉你们如何将它集成到他们的系统中
function readFile(file){
   var loader = new FileReader();
   var def = $.Deferred(), promise = def.promise();

   //--- provide classic deferred interface
   loader.onload = function (e) { def.resolve(e.target.result); };
   loader.onprogress = loader.onloadstart = function (e) { def.notify(e); };
   loader.onerror = loader.onabort = function (e) { def.reject(e); };
   promise.abort = function () { return loader.abort.apply(loader, arguments); };

   loader.readAsBinaryString(file);

   return promise;
}

function upload(url, data){
    var def = $.Deferred(), promise = def.promise();
    var mul = buildMultipart(data);
    var req = $.ajax({
        url: url,
        data: mul.data,
        processData: false,
        type: "post",
        async: true,
        contentType: "multipart/form-data; boundary="+mul.bound,
        xhr: function() {
            var xhr = jQuery.ajaxSettings.xhr();
            if (xhr.upload) {

                xhr.upload.addEventListener('progress', function(event) {
                    var percent = 0;
                    var position = event.loaded || event.position; /*event.position is deprecated*/
                    var total = event.total;
                    if (event.lengthComputable) {
                        percent = Math.ceil(position / total * 100);
                        def.notify(percent);
                    }                    
                }, false);
            }
            return xhr;
        }
    });
    req.done(function(){ def.resolve.apply(def, arguments); })
       .fail(function(){ def.reject.apply(def, arguments); });

    promise.abort = function(){ return req.abort.apply(req, arguments); }

    return promise;
}

var buildMultipart = function(data){
    var key, crunks = [], bound = false;
    while (!bound) {
        bound = $.md5 ? $.md5(new Date().valueOf()) : (new Date().valueOf());
        for (key in data) if (~data[key].indexOf(bound)) { bound = false; continue; }
    }

    for (var key = 0, l = data.length; key < l; key++){
        if (typeof(data[key].value) !== "string") {
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"; filename=\""+data[key].value[1]+"\"\r\n"+
                "Content-Type: application/octet-stream\r\n"+
                "Content-Transfer-Encoding: binary\r\n\r\n"+
                data[key].value[0]);
        }else{
            crunks.push("--"+bound+"\r\n"+
                "Content-Disposition: form-data; name=\""+data[key].name+"\"\r\n\r\n"+
                data[key].value);
        }
    }

    return {
        bound: bound,
        data: crunks.join("\r\n")+"\r\n--"+bound+"--"
    };
};

//----------
//---------- On submit form:
var form = $("form");
var $file = form.find("#file");
readFile($file[0].files[0]).done(function(fileData){
   var formData = form.find(":input:not('#file')").serializeArray();
   formData.file = [fileData, $file[0].files[0].name];
   upload(form.attr("action"), formData).done(function(){ alert("successfully uploaded!"); });
});