美文网首页前端开发那些事儿VUE学习
vue+axios+formData实现文件上传(包含简单的Ja

vue+axios+formData实现文件上传(包含简单的Ja

作者: 回忆不死我们不散 | 来源:发表于2020-05-14 14:47 被阅读0次

    <meta charset="utf-8">

    参考文章:

    用纯css美化<input type=file/>按钮
    vue+axios实现文件上传简单原理以及操作
    萌新用vue + axios + formdata 上传文件的爬坑之路
    从0开始做一个的Vue图片/ 文件选择(上传)组件[基础向]
    vue中利用axios实现文件上传进度实时更新
    Vue实现带进度条的文件拖动上传
    spring boot 文件上传
    阿里巴巴iconfont怎么是正确的使用方式?

    实现效果

    image

    1.用html和css画出文件上传组件

    参考文章用纯css美化<input type=file/>按钮并且利用里面的样式实现。
    其中文章的要点是:
    1.1文件上传使用<input type=file/>
    1.2组合label标签和<input type=file/>美化文件上传

    label好用在于,它可以跟input的click事件关联上,这就实现了语义化解决方案。因此点击这两个元素的任何一个都能得到相同的结果——弹出文件上传选择对话框。

    其中隐藏<input type=file/>标签的css为:

           .inputfile {
                width: 0.1px;
                height: 0.1px;
                opacity: 0;
                overflow: hidden;
                position: absolute;
                z-index: -1;
            }
    
    

    你可能会好奇为什么宽和高会设成0.1px而不是0px,因为在某些浏览器下0宽高将会让<input>元素被tab键忽略。而position: absolute的目的是不干扰随后元素的位置。

    1.3html+css实现静态页面代码

    <body>
        <div id="app" class="m-5">
            <div class="uploadBox">
                <h3>上传文件</h3>
                <div class="fileBox">
                    <input type="file" id="myFile" class="inputfile" @change="handlerUpload($event)">
                    <label for="myFile">
                        <i class="iconfont">&#xe632;</i>点击上传本地文件
                    </label>
                </div>
                <div class="fileInfo">
                    <ul class="files">
                        <li v-for="(file, index) in files">
                            {{ file.name }}
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </body>
    
    

    1.4实现效果
    1.5icon
    在静态页面中引用了阿里的icon库。引入方式可以参考:
    阿里巴巴iconfont怎么是正确的使用方式?
    详细的步骤为:搜索获取适合的图标->加入购物车->添加至项目->下载->将对应iconfont.css复制至css文件夹下引用即可

    image

    2.构造form'Data,使用axios上传文件

    在项目中使用axios上传文件,记得new一个纯净的axios或者考虑用ajax请求。因为axios在项目估计已经用了全局配置请求头等信息,这里的配置可能被全局请求头拦截,导致请求失败。
    2.1构造formData

     let param = new FormData();
     param.append("name", "wiiiiiinney");
    //通过append向form对象添加数据
     param.append("file", file);
    //FormData私有类对象,访问不到,可以通过get判断值是否传进去
     console.log(param.get("file"));
    
    

    2.2以下为全局请求头的配置

          let config = {
            //添加请求头
            headers: { "Content-Type": "multipart/form-data" },
            //添加上传进度监听事件
            onUploadProgress: e => {
              var completeProgress = ((e.loaded / e.total * 100) | 0) + "%";
              this.progress = completeProgress;
            }
          };
    
    

    2.2axios发送请求

     axios.post('http://127.0.0.1:8778/upload', param, config).then(
    function (response) 
    { console.log(response); })
    .catch(function (error) {
     console.log(error);
     });
    
    

    4.后端接收文件

    java接收代码:

    @Bean
        public CorsFilter corsFilter() {
            final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            final CorsConfiguration config = new CorsConfiguration();
            config.setAllowCredentials(true); // 允许cookies跨域
            config.addAllowedOrigin("*");// 允许向该服务器提交请求的URI,*表示全部允许。。这里尽量限制来源域,比如http://xxxx:8080 ,以降低安全风险。。
            config.addAllowedHeader("*");// 允许访问的头信息,*表示全部
            config.setMaxAge(18000L);// 预检请求的缓存时间(秒),即在这个时间段里,对于相同的跨域请求不会再预检了
    
            config.addAllowedMethod("*");// 允许提交请求的方法,*表示全部允许,也可以单独设置GET、PUT等
            /*
            config.addAllowedMethod("HEAD");
            config.addAllowedMethod("GET");// 允许Get的请求方法
            config.addAllowedMethod("PUT");
            config.addAllowedMethod("POST");
            config.addAllowedMethod("DELETE");
            config.addAllowedMethod("PATCH");
            */
            source.registerCorsConfiguration("/**", config);
            return new CorsFilter(source);
        }
    
    private static final Logger logger = LoggerFactory.getLogger(Application.class);
        @Bean
        MultipartConfigElement multipartConfigElement() {
            MultipartConfigFactory factory = new MultipartConfigFactory();
            factory.setLocation("d:/tmp");
            return factory.createMultipartConfig();
        }
    
        @RequestMapping(value = "/upload")
        @ResponseBody
        public String upload(@RequestParam("file") MultipartFile file,@RequestParam("name")String name) {
            logger.info("name: "+name);
            if (file.isEmpty()) {
                return "文件为空";
            }
            // 获取文件名
            String fileName = file.getOriginalFilename();
            logger.info("上传的文件名为:" + fileName);
            // 获取文件的后缀名
            String suffixName = fileName.substring(fileName.lastIndexOf("."));
            logger.info("上传的后缀名为:" + suffixName);
            // 文件上传路径
            String filePath = "d:/roncoo/ttt/";
            // 解决中文问题,liunx 下中文路径,图片显示问题
            //fileName = UUID.randomUUID() + suffixName;
            File dest = new File(filePath + fileName);
            // 检测是否存在目录
            if (!dest.getParentFile().exists()) {
                dest.getParentFile().mkdirs();
            }
            try {
                file.transferTo(dest);
                return "上传成功";
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "上传失败";
        }
    
    

    6.将上传信息反馈

    6.1给每个文件设置一个进度条
    用uploadPercentage记录上传进度,uploadPercentage的变化由axios的progress事件监听计算得出。

                            var item = {
                                name: tFiles[i].name,
                                uploadPercentage: 1,
                                size: this.formatFileSize(tFiles[i].size, 0),
                                uploadStatus: 0
                            }
                            console.log(item)
                            this.files.push(item);
    
    

    6.2给每个文件设置一个上传状态
    uploadStatus记录文件状态。

    状态码 含义
    0 初始状态
    1 上传中
    2 已上传
    -1 服务器错误
    -2 上传文件类型不符合要求
    -3 上传文件超出限制

    6.3检测函数大小的函数

                    checkFileSize:function(fileSize) {
                        //2M
                        const MAX_SIZE = 2 * 1024 * 1024;
                        if (fileSize > MAX_SIZE) {
                            return false;
                        }
                        return true;
                    }
    
    

    6.4检测文件类型的函数

                    checkFileType: function (fileType) {
                        const acceptTypes = ['xls', 'doc'];
                        for (var i = 0; i < acceptTypes.length; i++) {
                            if (fileType === acceptTypes[i]) {
                                return true;
                            }
                        }
                        return false;
                    }
    
    

    6.4格式化文件大小的函数

                   formatFileSize: function (fileSize, idx) {
                        var units = ["B", "KB", "MB", "GB"];
                        idx = idx || 0;
                        if (fileSize < 1024 || idx === units.length - 1) {
                            return fileSize.toFixed(1) +
                                units[idx];
                        }
                        return this.formatFileSize(fileSize / 1024, ++idx);
                    },
    
    

    7.全部代码

    <!doctype html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
        <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
        <style>
            * {
                margin: 0;
                padding: 0;
            }
    
            li {
                list-style: none;
            }
    
            /*引入阿里的icon*/
    
            @font-face {
                font-family: "iconfont";
                src: url('iconfont.eot?t=1534844614970');
                /* IE9*/
                src: url('iconfont.eot?t=1534844614970#iefix') format('embedded-opentype'), /* IE6-IE8 */
                url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAATgAAsAAAAAB1AAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8qEgkY21hcAAAAYAAAABLAAABcOdatkBnbHlmAAABzAAAASwAAAFY2/C2dWhlYWQAAAL4AAAALwAAADYSY0OKaGhlYQAAAygAAAAcAAAAJAfeA4NobXR4AAADRAAAAAgAAAAICAAAAGxvY2EAAANMAAAABgAAAAYArAAAbWF4cAAAA1QAAAAgAAAAIAEPAH1uYW1lAAADdAAAAUUAAAJtPlT+fXBvc3QAAAS8AAAAIwAAADQ9SsrveJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeGT0zYm7438AQw9zA0AAUZgTJAQDk9QxHeJxjYGBgZWBgYAZiHSBmYWBgDGFgZAABP6AoI1icmYELLM7CoARWwwISf2b0/z+MBPJZwCQDIxvDKOABkzJQHjisIJiBEQCjLgoxAHicHY3BTsJAGIR3drv/0hZaqXVLIKJSugVNQAOUxCKYeNKLMTHRB+Bi4sGTD2B8GV8C4zNxJepC/vxzmMzMxwRjfz/iW1wzn52xN8YQQLWRzDEdQJA60Co1C5gJhSAzLbZ3BB1TZwgjY2XTaceMixK2UZi8Y2u5Iesb+3ZIl9CJppTyAYaYFFObG21XEp3EI30EzlYbKTernX6B89ALAAFuvARwsiwI7sgPyXBo7p6ffLqe737Eh1K2Wxrifn65bKhKFjXbTtpS6rUu4LvlrFcSwL2y59Dpy/vjQMmZB7HYgRxnq78PwDqSMlpbHM9cv6pk1q3Vbin0KeNdomMDr+4h2W/2uF48+2o03kvhVPpF5gChW7WEoi8SonyIaNloPV3cyPoVZ+wfIBIx1XicY2BkYGAA4r3qZgbx/DZfGbhZGEDg+sL/xxD0/4MsDMwOQC4HAxNIFAA6yguGAHicY2BkYGBu+N/AEMPCAAJAkpEBFTABAEcIAmsEAAAABAAAAAAAAAAArAAAAAEAAAACAHEAAwAAAAAAAgAAAAoACgAAAP8AAAAAAAB4nGWPTU7DMBCFX/oHpBKqqGCH5AViASj9EatuWFRq911036ZOmyqJI8et1ANwHo7ACTgC3IA78EgnmzaWx9+8eWNPANzgBx6O3y33kT1cMjtyDRe4F65TfxBukF+Em2jjVbhF/U3YxzOmwm10YXmD17hi9oR3YQ8dfAjXcI1P4Tr1L+EG+Vu4iTv8CrfQ8erCPuZeV7iNRy/2x1YvnF6p5UHFockikzm/gple75KFrdLqnGtbxCZTg6BfSVOdaVvdU+zXQ+ciFVmTqgmrOkmMyq3Z6tAFG+fyUa8XiR6EJuVYY/62xgKOcQWFJQ6MMUIYZIjK6Og7VWb0r7FDwl57Vj3N53RbFNT/c4UBAvTPXFO6stJ5Ok+BPV8bUnV0K27LnpQ0kV7NSRKyQl7WtlRC6gE2ZVeOEXpc0Yk/KGdI/wAJWm7IAAAAeJxjYGKAAC4G7ICJkYmRmYGrOCMxLz05ozQxj4EBACJZBEAA') format('woff'), url('iconfont.ttf?t=1534844614970') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/
                url('iconfont.svg?t=1534844614970#iconfont') format('svg');
                /* iOS 4.1- */
            }
    
            .iconfont {
                font-family: "iconfont" !important;
                font-size: 16px;
                font-style: normal;
                -webkit-font-smoothing: antialiased;
                -moz-osx-font-smoothing: grayscale;
            }
    
            .icon-shangchuan:before {
                content: "\e632";
            }
    
            .iconfont {
                font-family: "iconfont";
                font-size: 18px;
                font-style: normal;
                -webkit-font-smoothing: antialiased;
                -webkit-text-stroke-width: 0.2px;
                -moz-osx-font-smoothing: grayscale;
                padding-left: 20px
            }
    
            .uploadBox {
                width: 400px;
                border: 1px solid #ccc;
                margin: 100px auto;
            }
    
            .fileBox,
            .fileInfo {
                margin: 16px;
                height: 60px;
                line-height: 60px;
                border: 1px solid #ccc;
                padding-left: 16px;
                font-size: 16px;
            }
    
            .inputfile {
                width: 0.1px;
                height: 0.1px;
                opacity: 0;
                overflow: hidden;
                position: absolute;
                z-index: -1;
            }
    
            /*E + F 毗邻元素选择器,匹配所有紧随E元素之后的同级元素F*/
    
            .inputfile+label {
                color: #3e97df;
                display: inline-block;
            }
    
            .inputfile:focus+label,
            .inputfile+label:hover {
                color: #0c89f0;
            }
    
            h3 {
                padding: 10px 0 0 16px;
                font-weight: normal;
                font-size: 18px;
                color: #666;
            }
    
            .filePart {
                line-height: 30px;
                overflow: hidden;
                float: left;
                text-overflow: ellipsis;
                white-space: nowrap;
                font-size: 12px;
                height: 30px;
            }
    
            .fileStatus {
                overflow: hidden;
                float: left;
                height: 20px;
                font-size: 10px;
                line-height: 20px;
            }
    
            .ml10 {
                margin-left: 10px;
            }
    
            .fileName {
                width: 200px;
            }
    
            .fileSize {
                width: 120px;
                text-align: center;
            }
    
            .uploadFail {
                color: #ff0800d3;
            }
    
            .uploadSuccess {
                color: #2c832c;
            }
    
            /*对应CSS*/
    
            .progress {
                position: relative;
                width: 80%;
                height: 8px;
                border: 1px solid #ccc;
                border-radius: 5px;
                overflow: hidden;
                /*注意这里*/
                box-shadow: 0 0 1px 0px #ddd inset;
            }
    
            .progress span {
                position: absolute;
                display: inline-block;
                width: 10%;
                height: 100%;
                background-color: #3e97df;
            }
        </style>
        <title>Document</title>
    </head>
    
    <body>
        <div id="app" class="m-5">
            <div class="uploadBox">
                <h3>上传文件</h3>
                <div class="fileBox">
                    <input type="file" id="myFile" class="inputfile" @change="handlerUpload($event)">
                    <label for="myFile">
                        <i class="iconfont">&#xe632;</i>点击上传本地文件
                    </label>
                </div>
                <ul class="files">
                    <li v-for="(file, index) in files">
                        <div class="fileInfo">
    
                            <div class="fileName filePart">
                                {{ file.name }}
                            </div>
                            <div class="fileSize filePart ml10">
                                {{file.size}}
                            </div>
                            <!--进度条-->
                            <div class="progress">
                                <span :style="{width:file.uploadPercentage,backgroundColor:file.uploadStatus==1 ||file.uploadStatus==2?'':'red'}"></span>
                            </div>
                            <div class="fileStatus">
                                <span v-if="file.uploadStatus == -1" class="uploadFail">出错啦,请重新上传或者删除</span>
                                <span v-if="file.uploadStatus == 2" class="uploadSuccess"> 已上传</span>
                                <span v-if="file.uploadStatus == 1" class="uploadSuccess"> 上传中...</span>
                                <span v-if="file.uploadStatus == -2" class="uploadFail">出错啦,文件类型不符合要求</span>
                                <span v-if="file.uploadStatus == -3" class="uploadFail">出错啦,文件大小超出限制</span>
                            </div>
                        </div>
                    </li>
                </ul>
            </div>
        </div>
        </div>
        <script>
            new Vue({
                el: '#app',
                data: {
                    files: [],
                    uploadSuccess: 0
                },
                methods: {
                    handlerUpload: function (e) {
                        //获取选定的文件
                        let tFiles = e.target.files;
                        let len = tFiles.length;
                        for (var i = 0; i < len; i++) {
                            //开始上传每一个文件
                            var item = {
                                name: tFiles[i].name,
                                uploadPercentage: 1,
                                size: this.formatFileSize(tFiles[i].size, 0),
                                uploadStatus: 0
                            }
                            console.log(item)
                            this.files.push(item);
                            //开始上传文件 新建一个formData
                            let param = new FormData();
                            param.append("name", "wiiiiiinney");
                            //通过append向form对象添加数据
                            param.append("file", tFiles[i]);
                            //FormData私有类对象,访问不到,可以通过get判断值是否传进去
                            console.log(param.get("file"));
                            //判断大小
                            if (!this.checkFileSize(tFiles[i].size)) {
                                item.uploadStatus = -3;
                                return false;
                            }
                            if (!this.checkFileType(tFiles[i].name.split('.')[1])) {
                                item.uploadStatus = -2;
                                return false;
                            }
                            //通过axios上传文件
                            //配置
                            let config = {
                                //添加请求头 
                                headers: {
                                    "Content-Type": "multipart/form-data"
                                },
                                //添加上传进度监听事件 
                                onUploadProgress: e => {
                                    var completeProgress = ((e.loaded / e.total * 100) | 0) + "%";
                                    console.log(this.files)
                                    item.uploadPercentage = completeProgress;
                                }
                            };
                            axios.post('http://127.0.0.1:8778/upload', param, config).then(function (
                                response) {
                                console.log(response);
                                item.uploadStatus = 2;
                            }).catch(function (error) {
                                console.log(error);
                                item.uploadStatus = -1;
                            });
                        }
                    },
                    formatFileSize: function (fileSize, idx) {
                        var units = ["B", "KB", "MB", "GB"];
                        idx = idx || 0;
                        if (fileSize < 1024 || idx === units.length - 1) {
                            return fileSize.toFixed(1) +
                                units[idx];
                        }
                        return this.formatFileSize(fileSize / 1024, ++idx);
                    },
                    checkFileType: function (fileType) {
                        const acceptTypes = ['xls', 'doc', 'jpg'];
                        for (var i = 0; i < acceptTypes.length; i++) {
                            if (fileType === acceptTypes[i]) {
                                return true;
                            }
                        }
                        return false;
                    },
                    checkFileSize: function (fileSize) {
                        //2M
                        const MAX_SIZE = 2 * 1024 * 1024;
                        if (fileSize > MAX_SIZE) {
                            return false;
                        }
                        return true;
                    }
                }
            });
        </script>
    </body>
    
    </html>
    
    

    8.参考代码、使用ajax上传文件、实现拖拽功能

    <!doctype html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcss.com/vue/2.5.13/vue.min.js"></script>
        <style>
            .dropbox {
                border: .25rem dashed #007bff;
                min-height: 5rem;
            }
        </style>
        <title>Document</title>
    </head>
    
    <body>
        <div id="app" class="m-5">
            <div class="dropbox p-3">
                <h2 v-if="files.length===0" class="text-center">把要上传的文件拖动到这里</h2>
                <div class="border m-2 d-inline-block p-4" style="width:15rem" v-for="file in files">
                    <h5 class="mt-0">{{ file.name }}</h5>
                    <div class="progress">
                        <div class="progress-bar progress-bar-striped" :style="{ width: file.uploadPercentage+'%' }"></div>
                    </div>
                </div>
            </div>
        </div>
        <script>
            new Vue({
                el: '#app',
                data: {
                    files: []
                },
                methods: {
                    uploadFile: function (file) {
                        var item = {
                            name: file.name,
                            uploadPercentage: 0
                        };
                        this.files.push(item);
                        var fd = new FormData();
                        fd.append('myFile', file);
    
                        var xhr = new XMLHttpRequest();
                        xhr.open('POST', 'http://127.0.0.1:8778/upload', true);
                        xhr.upload.addEventListener('progress', function (e) {
                            item.uploadPercentage = Math.round((e.loaded * 100) / e.total);
                        }, false);
                        xhr.send(fd);
                    },
                    onDrag: function (e) {
                        e.stopPropagation();
                        e.preventDefault();
                    },
                    onDrop: function (e) {
                        e.stopPropagation();
                        e.preventDefault();
                        var dt = e.dataTransfer;
                        for (var i = 0; i !== dt.files.length; i++) {
                            this.uploadFile(dt.files[i]);
                        }
                    }
                },
                mounted: function () {
                    var dropbox = document.querySelector('.dropbox');
                    dropbox.addEventListener('dragenter', this.onDrag, false);
                    dropbox.addEventListener('dragover', this.onDrag, false);
                    dropbox.addEventListener('drop', this.onDrop, false);
                }
            });
        </script>
    </body>
    
    </html>
    

    引用:https://www.jianshu.com/p/9c708a47d8a5

    相关文章

      网友评论

        本文标题:vue+axios+formData实现文件上传(包含简单的Ja

        本文链接:https://www.haomeiwen.com/subject/kfbznhtx.html