基于html5的多图片上传

基于html5的多图片上传

本文是建立在张鑫旭大神的多文图片传的基础之上.
首先先放出来大神多图片上传的博客地址:http://www.zhangxinxu.com/wordpress/2011/09/%E5%9F%BA%E4%BA%8Ehtml5%E7%9A%84%E5%8F%AF%E9%A2%84%E8%A7%88%E5%A4%9A%E5%9B%BE%E7%89%87ajax%E4%B8%8A%E4%BC%A0/

代码思路

因为html5开放了multiple, 可以让用户一次上传多个文件.
所以,最开始,我们需要建立这样的html.

<input type="file" name="fuck" multiple="" id="js-file">//上传文件区域
<div id="js-pre"></div>//图片预览区域
<button id="js-button" type="button">提交</button>//提交按钮

这个时候,我们就可以直接通过获得input里面的files(不明白的同学可以百度一下),然后循环.提交给服务器.但是,我们会遇到另外一个问题.如果,用户提交了一个图片,但是用户忽然觉得,我不想提交这个.我这个不要了,我要提交其他几个. 这种情况就非常的尴尬了.(用户真特么事逼┑( ̄Д  ̄)┍).
所以,我们需要一个数组,临时存储这些上传的数据.当用户删除之前上传的图片的时候,我们可以在那个临时的数组中,删除相应的数据.
打一个比方.用户通过input选择了ABC三个图片.
然后,我们循环input的files,然后依次把ABC加入到我们的临时数组x中.
这个时候,用户不想上传A图片了,他想删除A. 于是,贴心的我们,从临时数组删除A.
接下来,我们的图片预览以及上传图片都来自临时数组x的内容.这样,无论用户怎么折腾,贴心无比的我们,都可以满足用户的需求.
带着这样的思路.我们开始扯淡.


如果有懒惰党,可以直接下载代码使用.但是代码的地址在最下面哦~


完成这个多文件上传.我们要做到以下几点要求(所以是几点?)

  • 可以复用(这段代码可以在一个页面多次使用,不会相互冲突)
  • 我们要根据用户选择上传图片->完成文件的预览和删除
  • 用户选择提交代码的时候,进行文件传输,传输成功的时候,提供success(response){},提供完成相应的回调,同时也提供Failure(response){},进行提交失败的处理.以及Complete(){},提交完成的处理.

所以,我们从以上几点,一点一点的开始.


可以复用.
可以复用的意思是,使用者(不想造轮子的程序猿),可以在使用的时候,传递一个obj,里面带有以下参数

  • 用来上传文件的input的id
  • 触发提交的button的id
  • 预览图片的div的id
  • 后端的url
  • 唯一的文件名称,为了在html中与其他的多图片上传进行划分
  • 上传成功的函数
  • 上传失败的函数
  • 上传完成的函数
  • 对上传图片过滤的函数(可选) 可供使用者设置处理图片
    所以,我们可以设置一个obj.
    obj = {
        name: obj.name? obj.name:null,                              //唯一名,用来与其他文件区分
        fileInput: obj.fileInput?obj.fileInput: null,               //html file控件
        upButton: obj.upButton? obj.upButton:null,                  //提交按钮
        pre: obj.pre?obj.pre:null,                                  //预览地址
        url: obj.url? obj.url:null,                                 //ajax地址                    
        fileFilter: [],                                             //过滤后的文件数组
        filter: obj.filter?obj.filter:function(files){
            var arrFiles = [];
            for (var i = 0, file; file = files[i]; i++) {
                if (file.type.indexOf("image") == 0) {
                    arrFiles.push(file);     
                } else {
                    alert('文件"' + file.name + '"不是图片。');    
                }
            }
            return arrFiles;
        },                                                          //过滤的文件,默认判断如果是图片可以加入临时数组
        onSuccess: obj.onSuccess?obj.onSuccess:function(data){
            console.log('success');
        },                                                          //文件上传成功时
        onFailure: obj.onFailure?obj.onFailure:function(data){
            console.log('error');
        },                                                          //文件上传失败时,
        onComplete: obj.onComplete?obj.onComplete:function(){
            console.log('complete');
        },                                                          //文件全部上传完毕时
    }

然后我们需要一个函数,这个函数里面包含了我们生成多图片预览,删除,上传等功能.将obj传入到函数FFile().
这样,我们就可以通过 var fuck = new FFile(obj) 创建一个可以复用的多文件上传.
外部构造基本结束,接下来,就可以进行 FFile(){}的内部处理.

    function FFile(obj) {
    this.FileSet = {
        name: obj.name? obj.name:null,                              //唯一名,用来与其他文件区分
        fileInput: obj.fileInput?obj.fileInput: null,               //html file控件
        upButton: obj.upButton? obj.upButton:null,                  //提交按钮
        pre: obj.pre?obj.pre:null,                                  //预览地址
        url: obj.url? obj.url:null,                                 //ajax地址                    
        fileFilter: [],                                             //过滤后的文件数组
        filter: obj.filter?obj.filter:function(files){
            var arrFiles = [];
            for (var i = 0, file; file = files[i]; i++) {
                if (file.type.indexOf("image") == 0) {
                    arrFiles.push(file);     
                } else {
                    alert('文件"' + file.name + '"不是图片。');    
                }
            }
            return arrFiles;
        },                                                          //过滤的文件,默认判断如果是图片可以加入临时数组
        onSuccess: obj.onSuccess?obj.onSuccess:function(data){
            console.log('success');
        },                                                          //文件上传成功时
        onFailure: obj.onFailure?obj.onFailure:function(data){
            console.log('error');
        },                                                          //文件上传失败时,
        onComplete: obj.onComplete?obj.onComplete:function(){
            console.log('complete');
        },      
    }

    this.init();//一会再给你们说这个是干嘛的(笑)
}

接下来我们要处理的事情有两个.

  1. 我们要根据用户选择上传图片->完成文件的预览和删除
  2. 用户选择提交代码的时候,进行文件传输,传输成功的时候,提供success(response){},提供完成相应的回调,同时也提供Failure,complate.

首先是第一个

当用户上传文件后,会触发change事件.
然后,我们根据change事件,可以调用一个函数,这个函数把过滤后的图片(不符合图片要求),加入到临时数组fileFilter中.

        funGetFiles: function(e) {          
        // 获取文件列表对象
        var files = e.target.files || e.dataTransfer.files;
        //继续添加文件
        this.FileSet.fileFilter = this.FileSet.fileFilter.concat(this.FileSet.filter(files));
        this.funDealFiles();//下面讲解这个是啥(┑( ̄Д  ̄)┍)
        return this; //方便进行链式操作,类似jq的$().xx().xx().xx();
    },

因为,用户可能对上传的图片,进行删除.这样,我们要知道用户删除了哪个图片,我们才能在数组fileFilter中删除相应的图片.于是乎,我们就增加了一个索引值.

        //选中文件的处理与回调
    funDealFiles: function() {
        for (var i = 0, file; file = this.FileSet.fileFilter[i]; i++) {
            //增加唯一索引值
            file.index = i;
        }
        //执行选择回调
        this.onSelect(this.FileSet.fileFilter);
        return this;
    },

然后就要在预览区域显示图片,同时为预览的图片增加删除选项,如何获得上传图片的地址.我采用了FileReader,本文不讨论这个.想知道关于FileReader,请自行百度.

    /*
     * 对保存在fileFilter的图片,生成预览
     * @param file {array} 传递进来的含有所有file的数组
     */
    onSelect: function(files) {
        var html = '', 
            self = this,
            i = 0;
        $(this.FileSet.pre).html();//清空预览地址
        var funAppendImage = function() { //因为调用FileReader(),在FileReader.onload里面,进行的数值的修改,无法带到外面来,就像是,一个只允许进不允许出的盒子(原谅我不恰当的比喻),所以,可以通过递归的放置,来保存对var html的修改.
            file = files[i];

            if (file) {
                var reader = new FileReader()
                reader.onload = function(e) { //生成的图片
                    html += '<div id="js-uploadList_'+self.FileSet.name+i+'" class="img-box">';
                    html +=     '<div class="img-border">';
                    html +=         '<span class="js-upload_delete icon-delete_fill red" data-index="'+ i +'"></span>';
                    html +=         '![]('+e.target.result+')</img>';
                    html +=     '</div>';
                    html += '</div>';
                    
                    i++;
                    funAppendImage();
                }
                reader.readAsDataURL(file);
            } else {
                $(self.FileSet.pre).html(html);
                if (html) {
                    //删除方法
                    $(".js-upload_delete").click(function() {
                        self.funDeleteFile(files[parseInt($(this).attr("data-index"))]);
                        return false;   
                    }); 
                }
            }
        };
        funAppendImage();  
    },

接下来是删除的方法,这里分为两个步骤.一个是删除临时数组中的图片,另一个是删除html中的图片(在这里,利用了FileSet.name划分多个多图片上传.)

//删除对应的文件
    funDeleteFile: function(fileDelete) {
        var arrFile = [];
        for (var i = 0, file; file = this.FileSet.fileFilter[i]; i++) {
            if (file != fileDelete) {
                arrFile.push(file);
            } else {
                this.onDelete(fileDelete);  
            }
        }
        this.FileSet.fileFilter = arrFile;
        return this;
    },
    /*
     *删除函数
     *@param fileDelete {str} 传递过来要删除的File
     */
    onDelete: function(file){//清除图片
        let self = this;
        $("#js-uploadList_" + self.FileSet.name+ file.index).empty();
    },

然后是第二个

第二个使用了FormData来传输多文件的数据.不明白的小伙伴可以继续百度FormData.代码简单,直接阅读即可.

//文件上传
    funUploadFile: function() {
        var self = this;    
        if (location.host.indexOf("sitepointstatic") >= 0) {
            //非站点服务器上运行
            return; 
        }
        for (var i = 0, file; file = this.FileSet.fileFilter[i]; i++) {
            (function(file) {   
                var formData = new FormData();
                formData.append(self.FileSet.name,file);
                $.ajax({
                    url: self.FileSet.url,
                    type: 'post',
                    data: formData,
                    contentType: false,  
                    processData: false, 
                    success: function(data) {
                        self.FileSet.onSuccess(data);
                        self.funDeleteFile(file);
                    },
                    error: function(data) {
                        self.FileSet.onFailure(data);
                    }
                })
                if(!self.FileSet.fileFilter.length){
                    //执行完成
                    self.FileSet.onComplete();
                }
            })(file);   
        }   
            
    },

最后就是一开始提到的init()

就是初始化一些东西啦~~~

init: function() {
        var self = this;
        //判断必须参数是否为空
        if(self.FileSet.fileInput == null || self.FileSet.pre == null || self.FileSet.upButton == null) {
            console.log('必须提供 input的id 预览区的id 以及 button的id');
            return false;
        }
        //文件选择控件选择
        if (self.FileSet.fileInput) {
            // self.FileSet.fileInput.addEventListener("change", function(e) { self.funGetFiles(e); }, false);  
            $(self.FileSet.fileInput).on('change',function(e){
                self.funGetFiles(e);
            })
        }
        
        //上传按钮提交
        if (self.FileSet.upButton) {
            // self.FileSet.upButton.addEventListener("click", function(e) { self.funUploadFile(e); }, false);  
            $(self.FileSet.upButton).on('click', function(e) {
                self.funUploadFile(e);
            })
        } 

基本上,一个多图片上传的思路就是这样的~
以上.
另外这里是源代码地址.https://github.com/fankof29/duotupianshangchuan

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容

  • 单例模式 适用场景:可能会在场景中使用到对象,但只有一个实例,加载时并不主动创建,需要时才创建 最常见的单例模式,...
    Obeing阅读 2,045评论 1 10
  • 《ijs》速成开发手册3.0 官方用户交流:iApp开发交流(1) 239547050iApp开发交流(2) 10...
    叶染柒丶阅读 5,009评论 0 7
  • 工厂模式类似于现实生活中的工厂可以产生大量相似的商品,去做同样的事情,实现同样的效果;这时候需要使用工厂模式。简单...
    舟渔行舟阅读 7,697评论 2 17
  • 想杀人怎么办
    玉玲琅阅读 222评论 0 1
  • 今天我们去年画村看年画,从山边出发到年画村,我们坐在车数看能有多少幅年画,一直到年画村我共数了320幅年画,当时我...
    暗香疏影0阅读 695评论 0 0