• 每日技术总结:可拖拽文件批量上传


    前言:

    介绍拖拽文件和选择文件两种操作,获取文件信息以及上传服务器的思路和流程

    项目前端环境:vue,jquery

    可拖拽文件批量上传

    刚开始我是直接用的element-ui的上传组件,里面自带一个简单的拖拽属性,后来发现完全满足不了我们项目的各种奇葩需求。于是开始尝试从0手写一个可拖拽文件批量上传的功能。

    1.布局Html代码:

    <!--测试demo-->
    <div class="file-upload-demo">
        <!--拖拽区域-->
        <div class="drag-wrap">
            <div ref="select_frame" class="drag-inner">
                <!--无内容时提示信息-->
                <div v-show="fileList.length<1" class="tip">拖拽上传简历文件</div>
                <!--显示上传的文件列表-->
                <div v-show="fileList.length" class="filebox scroll-wrap">
                    <ul>
                        <li v-for="item in fileList" v-bind:class="'file-'+item.state">
                            <i class="fa fa-file-text-o"></i> {{item.name}}
                            <!--状态:上传失败/上传成功-->
                            <span class="file-state" v-if="item.state">
                                <span v-if="item.state == 'failure'" v-bind:title="item.msg">上传失败 <i class="fa fa-times-circle"></i></span>
                                <span v-else-if="item.state == 'success'">上传成功 <i class="fa fa-check-circle"></i></span>
                                <span v-else>未知</span>
                            </span>
                            <!--状态:上传中-->
                            <span class="file-state" v-else>
                                上传中...
                            </span>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
        <!--按钮-->
        <button class="btn_save" v-on:click="openFileIpt" style="margin-top: 10px;">选择文件</button>
        <!--隐藏文件域-->
        <input ref="fileIpt" type="file" v-bind:multiple="isMultiple" v-show="false" name="file" v-on:change="inputFiles" />
    </div>

    这个布局是根据我当前项目需求来的,没有剥离出最简单版。凑合看吧~

    目前效果如下:

    2.在vue实例的mounted钩子函数里,给select_frame这个元素写拖拽的四个事件:ondragleave,ondrop,ondragenter,ondragover

    这就是为什么需要给拖拽元素写一个ref

    <div ref="select_frame" class="drag-inner">
        ...
    </div>

    js代码如下:

    mounted ()
        {
            //文件拖拽上传
            this.$refs.select_frame.ondragleave = (e) => {
                e.preventDefault();  //阻止离开时的浏览器默认行为
            };
            this.$refs.select_frame.ondrop = (e) => {
                e.preventDefault();    //阻止拖放后的浏览器默认行为
                this.fileList = [];
    
                var files = e.dataTransfer.files;  // 获取文件对象
    
                if (files.length < 1) return;
    
                //调用上传方法
                if (this.isMultiple) {
                    this._uploadFiles(files);
                } else {
                    this._uploadFile(files[0]);
                }
            };
            this.$refs.select_frame.ondragenter = (e) => {
                e.preventDefault();  //阻止拖入时的浏览器默认行为
                this.$refs.select_frame.border = '2px dashed red';
            };
            this.$refs.select_frame.ondragover = (e) => {
                e.preventDefault();    //阻止拖来拖去的浏览器默认行为
            };
    
        },

    首先需要阻止浏览器默认行为

    拖拽的四个事件方法里都可以拿到event对象,选择的文件信息就在event对象里。

    var files = e.dataTransfer.files;  // 获取文件对象

    3.这里因为项目需求是既需要批量选择也需要单选,根据入口不同来切换内容。这两个功能我共用了一段代码,只用isMultiple来控制单选还是多选。作为案例有点不妥。勉强看看吧~

    拿到文件信息后开始调用上传方法,上传方法里包括文件数量、文件大小、文件格式等校验代码以及提交服务器的ajax代码,methods钩子函数里面批量上传方法如下:

    //批量文件上传
    _uploadFiles: function (files) {
        var _this = this;
        var len = files.length;
        var accept = ['.html', '.doc', '.docx', '.htm', '.wps']; //文件格式
        var fileSize = 1024; //文件大小1M
    
        //校验文件数量,超过8个取前8个,并给出提示,不影响流程
        if (len > 8) {
            len = 8;
            _this.$message.error('上传文件不可超过8个');
        }
    
        //创建一个FormData实例用来保存文件信息
        var formData = new FormData();
    
        //遍历文件校验类型和大小
        for (let i = 0; i < len; i++) {
    
            //文件类型验证
            var verifyType = accept.some(function (item) {
                return files[i].name.indexOf(item) > -1;
            })
            //文件size验证
            var thisFileSize = files[i].size / 1024; //因为拿到的文件大小单位是B,需要除以1024换算成KB
            var verifySize = (thisFileSize < fileSize);
            
            if (!verifyType) {
                _this.$message.error(files[i].name + '文件格式错误');
            } else if (!verifySize) {
                _this.$message.error(files[i].name + '文件大小不可超出1M');
            } else {
                formData.append('file', files[i]);
                this.fileList.push(files[i]);
            }
        }
    
        if (this.fileList.length < 1) return;
        $.ajax({
            url: '/xxx/Candidate/AnalysisResumeBatch?candidatetype=1',
            type: 'post',
            data: formData,
            dataType: 'json',
            contentType: false,
            processData: false,
            beforeSend: function () {
                _this.loading = true;
            },
            success: function (res) {
                if (res.IsSuccess) {
                    console.log(res.Data);
                    var data = res.Data;
    
                    _this.fileList.forEach(function (file) {
                        var name = file.name;
                        for (let i = 0; i < data.length; i++) {
                            if (name != data[i].FileName) {
                                continue;
                            } else {
                                file.state = data[i].Status;
                                file.msg = data[i].InfoMsg;
                                return false;
                            }
                        }
                    })
    
                } else {
                    _this.$message.error(res.Msg);
                }
            },
            error: function () {
                _this.$message.error('请求失败');
            },
            complete: function () {
                _this.loading = false;
            }
        })
    },

    ajax success回调代码有点复杂,这是因为返回的数据问题,每个项目要根据返回数据来处理。这里可以忽略,不作说明。

    之所以把单个上传方法和批量上传方法分开是因为,这两者的校验复杂度不一样,异步回调处理也不一样。为了代码更好理解和维护,就这么做了。其实还可以优化下。

    到这里就算一个完整的功能了,但我必须再提一种获取文件信息的方法,点击按钮获取文件信息。

    6.按钮代码如下:

    <!--按钮-->
        <button class="btn_save" v-on:click="openFileIpt" style="margin-top: 10px;">选择文件</button>
        <!--隐藏文件域-->
        <input ref="fileIpt" type="file" v-bind:multiple="isMultiple" v-show="false" name="file" v-on:change="inputFiles" />

    首先,之所以放2个元素是为了美化上传文件控件。这里就需要通过按钮来触发文件域的选择操作。这个技巧经常用到。

    首先给文件域绑定一个ref,v-show="false"默认隐藏

    <input ref="fileIpt" type="file" v-bind:multiple="isMultiple" v-show="false" name="file" v-on:change="inputFiles" />

    7.按钮上绑定的点击事件方法 openFileIpt

            //点击按钮触发文件域的点击事件
            openFileIpt: function () {
                this.$refs.fileIpt.click();
            },

    通过ref找到文件域,触发它的click();

    这一步点按钮和点文件域input是一样的效果了。

    8.现在开始写文件上传事件 inputFiles

            //文件域改变后执行上传文件
            inputFiles: function () {//通过上传文件域对象拿到文件信息集合var files = this.$refs.fileIpt.files;
    
                if (files.length < 1) return;
    
                //调用上传方法
                if (this.isMultiple) {
                    this._uploadFiles(files);
                } else {
                    this._uploadFile(files[0]);
                }
            }

    选择的文件信息就藏在this.$refs.fileIpt.files里面,后面的步骤跟拖拽时一样调用上传方法。

    结尾:

    本文重在讲解思路和流程,代码不是很完整,有些变量需要在data里定义,没有贴出来。

  • 相关阅读:
    如何用StatSVN统计SVN服务器某项目的代码量
    探秘JVM的底层奥秘
    SpingMVC流程图
    NioCopy文件
    我的angularjs源码学习之旅3——脏检测与数据双向绑定
    我的angularjs源码学习之旅2——依赖注入
    我的angularjs源码学习之旅1——初识angularjs
    IE兼容性问题汇总【持续更新中】
    nodejs学习笔记四——express-session
    我理解的this
  • 原文地址:https://www.cnblogs.com/cathy1024/p/10875383.html
Copyright © 2020-2023  润新知