最近在做项目时,遇到一个需求是,上传文件过程中需要判断下是否已经存在该文件,如果存在,让用户来决定是否继续上传,这里边有两个注意点:

  1. 是否存在该文件,需要调取后台接口返回结果来,涉及一个异步问题;
  2. 是否继续上传,需要在上传回调函数中通过return false来阻止文件继续上传。

首先我的思路是在before回调中做中断,我采用的layui版本是v2.5.6,在更低的版本中需要改下upload.js源码,
y=function(){return "choose" === i ? l.choose && l.choose(g) : ((l.before && l.before(g)) === false ? '' : o.ie ? o.ie > 9 ? u() : c() : void u()) };
然后在页面中按部就班写下upload相关业务代码:

upload.render({
    elem: '#chooseBtn',
    url: 'XXXXX',
    accept: 'file',
    exts: 'xls|excel|xlsx',
    headers: {
        token: localStorage.getItem('token')
    },
    data: {},
    before: function(obj) {
        var flag = true;
        layer.load(); //上传loading
        files = obj.pushFile();
        this.data = {
            institution: $("#dropdownMenu1").val(),
            handle_class: $("#handle_class").val(),
            file_name: files[fileId].name
        }
        obj.preview(function (index, file, result) {
            file_name = file.name
            fileId = index;
            $("#fileWrap").append('<li id="' + fileId + '"><span>' + file.name +
                '上传中</span><i class="layui-icon layui-icon-close"></i></li>'
            )
            $.ajax({
                url: 'mon/monitor/clean/checkUpload.json',
                contentType: 'application/x-www-form-urlencoded',
                type: 'post',
                beforeSend: function (request) {
                    request.setRequestHeader('token', localStorage.getItem('token'));
                },
                data: {
                    institution: $("#dropdownMenu1").val(),
                    file_name: file.name
                },
                dataType: "json", //服务器返回数据的类型为json  
                success: function (data) {
                    if (data.code == '100') {
                        $('#chooseBtn').css('display', 'none')
                        flag = true
                    } else {
                        if (data.code == '104') {
                            layer.confirm(data.msg + ',点击确定将替换原文件', {
                                title: '提示',
                                btn: ['取消', '确定']
                            },
                            function (index) {
                                //do something
                                delete files[fileId];
                                $("#fileWrap").find('li[id="' + fileId + '"]').remove();
                                layer.close(index);
                                flag = false
                                return false
                            },
                            function (index) {
                                flag = true
                                obj.upload(fileId, file)
                                $('#chooseBtn').css('display', 'none')
                            });
                            return false
                        } else {
                            flag = false
                            layer.msg(data.msg)
                        }
                    }
                },
                error: function (data) {
                    flag = false
                    console.error(data)
                }
            })
        });
        return flag
    },
    done: function (res, index, upload) {
        console.log(res, index)
        layer.closeAll('loading'); //关闭loading
    },
    error: function (index, upload) {
        layer.closeAll('loading'); //关闭loading
        delete files[index];
        $("#fileWrap").find('li[id="' + index + '"]').remove();
        $("#chooseBtn").css('display', 'inline-block')
    }
});

这样做看似没有问题,但是实际上选完文件之后就立即上传了,而去重判断的接口甚至是文件已经上传成功了才返回了结果,在上传文件前的flag读取的其实还是初始值true,因此并没有阻止上传进程,后来又尝试将去重改为同步async:false,又将去重整个赋值给一个函数,最后在before中return这个函数,结果就是flag虽然变成了期望的值false,但是依然没有阻止上传进程...
多次尝试之后,灵机一动既然立即上传不行,那可以试试触发按钮上传,这样做的好处是,选择文件其实只执行了choose这个选择文件回调,当点击了设置的这个上传开关按钮之后才会执行before,done,error这些回调,因此最终的解决思路就是:

  1. 选择文件后立马做(choose回调中)去重判断,并保存判断的结果;
  2. 点击上传按钮后立马决定(拿着去重结果)是否继续上传

代码仅供参考

var files = null;
var fileId = '';
var file_name = '';
var flag = true;
var uploadInst =  upload.render({
    elem: '#chooseBtn',
    url: 'XXX',
    accept: 'file',
    auto: false,
    bindAction: '#startBtn',
    exts: 'xls|excel|xlsx',
    headers: {
        token: localStorage.getItem('token')
    },
    data: {},
    xhr: xhrOnProgress,    //传入监听函数!important
    choose: function(obj) {
        $("#startBtn").css('display', 'inline-block')
        files = obj.pushFile();
        obj.preview(function (index, file, result) {
            file_name = file.name
            fileId = index;
            $("#fileWrap").append('<li id="' + fileId + '"><span>' + file.name +'上传中</span><i class="layui-icon layui-icon-close"></i><div class="layui-progress layui-progress-big" lay-filter="progressBar'+fileId+'" lay-showPercent="yes"><div class="layui-progress-bar layui-bg-red" lay-percent="0%"></div></div></li>')
            element.render('progress', 'progressBar'+fileId);
            $.ajax({
                url: 'xxx',
                contentType: 'application/x-www-form-urlencoded',
                type: 'post',
                async: false,
                beforeSend: function (request) {
                    request.setRequestHeader('token', localStorage.getItem('token'));
                },
                data: {
                    institution: $("#dropdownMenu1").val(),
                    file_name: file.name
                },
                dataType: "json",
                success: function (data) {
                    if (data.code == '100') {
                        $('#chooseBtn').css('display', 'none')
                        flag = true
                    } else {
                        if (data.code == '104') {
                            layer.confirm(data.msg + ',点击确定将替换原文件', {
                                title: '提示',
                                btn: ['取消', '确定']
                            },
                            function (index) {
                                //do something
                                delete files[fileId];
                               $("#fileWrap").find('li[id="' + fileId + '"]').remove();
                                layer.close(index);
                                flag = false
                                return false
                            },
                            function (index) {
                                flag = true
                                obj.upload(fileId, file)
                              $('#chooseBtn').css('display', 'none')
                            });
                            return false
                        } else {
                            flag = false
                            layer.msg(data.msg)
                        }
                    }
                },
                error: function (data) {
                    flag = false
                    console.error(data)
                }
            })
        });
    },
    before: function (obj) {
        layer.load(); //上传loading
        console.log('this:', this)
        this.data = {
            institution: $("#dropdownMenu1").val(),
            handle_class: $("#handle_class").val(),
            file_name: files[fileId].name
        }
        return flag
    },
    progress: function(n, elem){
        var percent = n + '%' //获取进度百分比
        element.progress('progressBar'+fileId, percent); //可配合 layui 进度条元素使用
    },
    done: function (res, index, upload) {
        console.log(res, index)
        layer.closeAll('loading'); //关闭loading
        if (files) {
            delete files[index];
        }
        if (res.code == '100') {
            fileSuccess = true
            $("#fileWrap li span")[0].innerText = file_name
        } else {
            layer.msg(res.msg)
            $("#fileWrap").find('li[id="' + fileId + '"]').remove();
            fileSuccess = false
        }
        $("#startBtn").css('display', 'none');
        $('#chooseBtn').css('display', 'inline-block');
    },
    error: function (index, upload) {
        layer.closeAll('loading'); //关闭loading
        delete files[index];
        $("#fileWrap").find('li[id="' + index + '"]').remove();
        $("#chooseBtn").css('display', 'inline-block')
    }
});
$("#fileWrap").on('click', 'li i', function(e) {
    var fileId = $(this).parent('li').attr("id");
    delete files[fileId];
    $(this).parent('li').remove();
    $("#chooseBtn").css('display', 'inline-block')
});

页面如图:

点击上传开关后,点击确定之后还需才会上传,否则重新选择文件,再次触发choose回调,进行去重判断...
上传成功后隐藏了上传开关

再多一点废话:进度条
同样是在upload.js文件中需要在p.prototype.config中加上xhr,即
p.prototype.config={accept:"images",exts:"",auto:!0,bindAction:"",url:"",field:"file",acceptMime:"",method:"post",data:{},drag:!0,size:0,number:0,multiple:!1,xhr: function() {}}
然后创建进度条监听函数

var xhrOnProgress = function (fun) {
    xhrOnProgress.onprogress = fun; //绑定监听
    //使用闭包实现监听绑
    return function () {
        //通过$.ajaxSettings.xhr();获得XMLHttpRequest对象
        var xhr = $.ajaxSettings.xhr();
        //判断监听函数是否为函数
        if (typeof xhrOnProgress.onprogress !== 'function')
            return xhr;
        //如果有监听函数并且xhr对象支持绑定时就把监听函数绑定上去
        if (xhrOnProgress.onprogress && xhr.upload) {
            xhr.upload.onprogress = xhrOnProgress.onprogress;
        }
        return xhr;
    }
}

进度条在选择文件的预览方法已经追加了dom,但是在此必须激活一下,否则会出现的情况是进度条虽然动态变化了,但是上面的文字也就是百分数没有跟着变,因此需这行:
element.render('progress', 'progressBar'+fileId);
然后在progress回调中更新进度就好了
今天就分享这么多吧,有更好的方案一起讨论哟~~~

03-05 21:25