spring boot 用js实现上传文件(包含其他字段)显示进度

简介: spring boot 用js实现上传文件(包含其他字段)显示进度

1、先看最终实现效果:

开始:

image.png

保存中:

image.png

保存结束:还在本页面,同时有提示,上面已经更新了内容。

image.png

具体实现步骤:


1、由于我使用了框架内置对象处理二进制文件信息,所有接收过程完全被封闭在框架内置对象中,直到请求结束才返回信息提示,上传的过程进度无法访问。我使用了js,不通过form表单action跳转后台上传。


(1)html内容如下;测试期间只需要关注那几个上传的字段和按钮·就好了,我们在js中只需要用到字段id获得信息,通过js上传。其他的都是布局和样式。

 <!--上传课节部分-->
            <div class="row">
                <!-- /.col -->
                <div class="col-md-12">
                    <div class="nav-tabs-custom">
                        <div class="tab-content box-body">
                            <h4 class="text-center">上传课节</h4>
                            <!--<form class="form-horizontal" action="/savenewcoursecontent" method="post" id="myform" enctype="multipart/form-data">-->
                                <div class="form-group">
                                    <input type="hidden" name="courseid" id="courseid" th:value="${course.getCourseid()}">
                                    <label  class="col-sm-2 control-label">课节名称</label>
                                    <div class="col-sm-10">
                                        <input type="text" class="form-control"  placeholder="请输入课节名称"
                                               name="contentname" id="contentname">
                                        <span></span>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <p></p>
                                    <label  class="col-sm-2 control-label">课节资源</label>
                                    <div class="col-sm-10">
                                        <input type="file"  name="img" th:id="file" th:onchange="checkimgaaa()"><span></span><br>
                                        <div class="col-lg-5"
                                             style="padding-left: 0; padding-right: 0; margin-bottom: 0px;">
                                            <div class="progress progress-striped active" style="display:">
                                                <div id="progressBar" class="progress-bar progress-bar-success"
                                                     role="progressbar" aria-valuemin="0" aria-valuenow="0"
                                                     aria-valuemax="100" style="width: 0%"></div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                                <div class="form-group">
                                    <label class="col-sm-2 control-label">章节名称</label>
                                    <div class="col-sm-10">
                                        <select name="chapterid" th:id="chapterid">
                                            <option th:each="u:${chapternames}"
                                                    th:value="${u.chapterid}"
                                                    th:text="${u.chaptername}">
                                            </option>
                                        </select>
                                    </div>
                                </div>
                                <div class="form-group" style="text-align: center">
                                    <div class="col-sm-offset-2 col-sm-12">
                                        <button type="submit" class="btn btn-primary " name="submit" id="mysave">提交保存</button>
                                        <button  type="button" name="qv" id="cancal" style="display: none"></button>
                                        <button type="button" class="btn btn-primary " onclick="end()">上传结束</button>
                                    </div>
                                </div>
                            <!--</form>-->
                            <!-- /.tab-pane -->
                        </div>
                        <!-- /.tab-content -->
                    </div>
                    <!-- /.nav-tabs-custom -->
                </div>
                <!-- /.col -->
            </div>

(2)导入封装的js文件,内容如下:需要根据需要进行调整和修改上传成功和失败后自定义的操作以及参数内容,后面会提到。代码意思自行理解。代码如下:

/**
 * 上传文件公共组件
 *
 * @param url 上传地址
 * @param processBar 进度条 jquery获取的页面组件
 * @param speedLab 显示上传速度Label jquery获取的页面组件
 * @param uploadBtn 上传按钮 jquery获取的页面组件
 * @param cancelBtn 取消上传按钮  jquery获取的页面组件
 * @param callBack 上传完成回调函数 上传完成后的回调函数,可以不传
 * @author
 * @returns
 */
function UploadCommon(url, processBar, speedLab, uploadBtn, cancelBtn,courseid, callBack) {
    function init() {
        // 每次回调监测上传开始时间
        var startTime = null
        // 已上传文件大小
        var oloaded = null
        var xhr = new XMLHttpRequest()
        function setProgress(w) {
            processBar.width(w + '%');
            processBar.text(w + '%');
        }
        function progressMonitor(evt) {
            if (evt.lengthComputable) {
                var completePercent = Math.round(evt.loaded / evt.total * 100)
                setProgress(completePercent)
                var nowTime = new Date().getTime()
                // 计算出上次调用该方法时到现在的时间差,单位为s
                var pertime = (nowTime - startTime) / 1000
                // 重新赋值时间,用于下次计算
                startTime = new Date().getTime()
                // 计算该分段上传的文件大小,单位b
                var perload = evt.loaded - oloaded
                // 重新赋值已上传文件大小,用以下次计算
                oloaded = evt.loaded
                // 上传速度计算,单位b/s
                var speed = perload / pertime
                var bspeed = speed
                // 单位名称
                var units = 'bit/s'
                if (speed / 1024 > 1) {
                    speed = speed / 1024
                    units = 'Kb/s'
                }
                if (speed / 1024 > 1) {
                    speed = speed / 1024
                    units = 'Mb/s'
                }
                speed = speed.toFixed(1);
                // 剩余时间
                var resttime = ((evt.total - evt.loaded) / bspeed).toFixed(1)
                speedLab.html(speed + units + ',剩余时间:' + resttime + 's')
            }
        }
        // 上传成功后回调
        function uploadComplete(evt) {
            uploadBtn.attr('disabled', false)
            var status = evt.currentTarget.status
            if (status == 401) {
                alert('请登录后再上传')
                return
            }
            if (status == 403) {
                alert('无权限操作')
                return
            }
            if (status != 200) {
                alert('上传异常,错误码' + status)
                return
            }
            var response = JSON.parse(evt.currentTarget.response);
            if (response != 1) {
                alert(1000)
            }else{
                window.location.href="/tosavecontentname?i=1"+ "&courseid=" + courseid;
            }
            if (callBack != null && typeof callBack != 'undefined') {
                callBack()
            }
        };
        // 上传失败回调
        function uploadFailed(evt) {
            alert('上传处理失败' + evt.target.responseText)
        }
        // 终止上传
        function cancelUpload() {
            xhr.abort()
        }
        // 上传取消后回调
        function uploadCanceled(evt) {
            alert('文件上传已终止:' + evt.target.responseText)
        }
        // 添加取消上传事件
        cancelBtn.click(function () {
            uploadBtn.attr('disabled', false)
            cancelUpload();
        })
        this.uploadFile = function (formData) {
            // 上传按钮禁用
            uploadBtn.attr('disabled', true);
            setProgress(0)
            // true为异步处理
            xhr.open('post', url, true)
            // 上传开始执行方法
            xhr.onloadstart = function () {
                console.log('开始上传')
                // 设置上传开始时间
                startTime = new Date().getTime()
                // 已上传的文件大小为0
                oloaded = 0
            }
            xhr.upload.addEventListener('progress', progressMonitor, false)
            xhr.addEventListener("load", uploadComplete, false)
            xhr.addEventListener("error", uploadFailed, false)
            xhr.addEventListener("abort", uploadCanceled, false)
            xhr.send(formData);
        }
        this.setProgressValue = function (w) {
            processBar.width(w + '%')
            processBar.text(w + '%')
        }
    }
    return new init()
}

(3)在html页面调用该组件,并根据自己操作的字段进行设置。具体含义代码中介绍。

<script type="text/javascript">
    $(document).ready(function() {
        var addVersionBtn=$('#mysave')  //<button>//点击上传的按钮
        var cancelUploadBtn=$('#cancal') //<button>//取消的按钮,这次我没用
        var fileInput=$("#file")  //<input>//获得文件的id
        var processBar = $("#progressBar"); //获得显示的进度条的id
        var speedLab=$("#showInfo") //<label>
        var courseid=$('#courseid').val();//我在上面的js组件中需要这个字段,在成功后进行跳转,所以给组件加了参数,在commons.js中加了
        var url='/savenewcoursecontent'//我后台上传内容的地址。
        //获取文件上传实例,调用组件
        var upload=UploadCommon(url,processBar,speedLab,addVersionBtn,cancelUploadBtn,courseid);
        // 点击上传按钮后触发的事件
        addVersionBtn.click(function(){
        //获得这些需要上传字段的数据
            var courseid=$('#courseid').val()
            var file = fileInput.get(0).files[0]
            var contentname=$('#contentname').val()
            var chapterid=$('#chapterid').val()
        //对字段的内容进行判断,限制内容的格式等
            if(file==null){
                alert("视频文件不能为空")
                return
            }
            if(courseid==''){
                alert("courseid不能为空");
                return
            }
            if(contentname==''){
                alert("课程名字不能为空")
                return
            }
            if(chapterid==''){
                alert("章节名不能为空")
                return
            }
            // 创建提交数据,将数据添加到formData中,然后上传
            var formData = new FormData();
            formData.append('img', fileInput.get(0).files[0]);
            formData.append('courseid', courseid);
            formData.append('contentname',contentname);
            formData.append('chapterid', chapterid);
            // 上传文件
            upload.uploadFile(formData)
        })
    });
</script>

我上次的是视频文件,所以加了下面的代码进行限制mp4格式,也可以在上面对字段进行判断中添加。

 //对上传的视频文件类型进行限制
    function checkimgaaa() {
        var aa = document.getElementById("file").value.toLowerCase().split(".");
        /*以.分割文件字符串*/
        if (aa[aa.length - 1] == 'mp4') {
        } else {
            document.getElementById("file").value = '';
            alert("格式不对,只能上传.mp4的视频");
        }
    }

2、下面进行后台处理。由于我是使用spring boot +thymeleaf+Mysql数据库进行开发,其他开发可能不一样。仅供参考

/**
     * 保存新增的课节信息
     */
    @RequestMapping(value = "/savenewcoursecontent", method = RequestMethod.POST)
    @ResponseBody
    public int aasavechapteradfad(MultipartFile img, String contentname,
                                          String chapterid, String courseid) {
        ModelAndView m = new ModelAndView();
        Date date = new Date();
        SimpleDateFormat sd1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String contenttime = sd1.format(date);
        //保存课程图片
        String filename = null;
        String path = ClassUtils.getDefaultClassLoader().getResource("").getPath() + "static/video";
        //给图片命名简单随机
        Random random = new Random();
        filename = System.currentTimeMillis() + random.nextInt(100000) * 100 + img.getOriginalFilename();
        File f = new File(path, filename);
        if (!f.exists()) {
            try {
                f.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            img.transferTo(f);
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //更新数据库的图片字段
        String contentpath = "/video/" + filename;
        CourseContent courseContent = new CourseContent();
        courseContent.setChapterid(Integer.valueOf(chapterid));
        courseContent.setCourseid(Integer.valueOf(courseid));
        courseContent.setContentname(contentname);
        courseContent.setContenttime(contenttime);
        courseContent.setContenturl(contentpath);
        //将课节保存进去数据库
        int i = cs.savecoursecontent(courseContent);
        return i;
    }

(2)我们可以根据效果演示看到,成功后还在本页面,而且提示了上传成功。那是我们上传完成后,返回了int类型的i,在上面提到的组件中,进行了成功的跳转。重复刚才组件中的这行。返回1,就直接进行跳转,带着我们第一次到此页面的内容和该值1。

 var response = JSON.parse(evt.currentTarget.response);
            if (response != 1) {
                alert("上传失败")
            }else{
                window.location.href="/tosavecontentname?i=1"+ "&courseid=" + courseid;
            }

在这个上传课节的页面最下面有我写了js,来接收i的值,并进行alert,没有值不响应,该内容必须在最下面,否则第一次进来不带i值,下面的js都无效了。

    <!--页面独有的js-->
    var i=[[${i}]];
    if(i==1){
        alert("上传成功!");
    }else {
        alert("保存失败!")
    }

返回0直接alert上传失败。如果其他页面也有上传文件可视化操作,我们可以在成功后返回不同的响应值,进行不同的跳转。失败都返回0,直接alert失败。当然跳转不同的页面需要不同的跳转数据,我们可以在组件方法参数上进行添加。不需要的在调用时赋值空就行了。

目录
相关文章
|
8天前
|
JavaScript 前端开发 Java
springboot从控制器请求至页面时js失效的解决方法
springboot从控制器请求至页面时js失效的解决方法
17 0
springboot从控制器请求至页面时js失效的解决方法
|
8天前
|
JSON JavaScript 前端开发
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
55 0
|
8天前
|
JavaScript
JS实现百分比进度球
JS实现百分比进度球
38 1
|
8天前
|
人工智能 JavaScript 前端开发
毕业设计|基于Spring Boot和Vue.js的前后端分离商城系统(二)
毕业设计|基于Spring Boot和Vue.js的前后端分离商城系统
|
8天前
egg.js 24.13sequelize模型-字段限制排序分页
egg.js 24.13sequelize模型-字段限制排序分页
35 1
egg.js 24.13sequelize模型-字段限制排序分页
|
8天前
|
JavaScript 前端开发
springboot+layui从控制器请求至页面时js失效的解决方法
springboot+layui从控制器请求至页面时js失效的解决方法
17 0
|
5天前
|
监控 安全 NoSQL
采用java+springboot+vue.js+uniapp开发的一整套云MES系统源码 MES制造管理系统源码
MES系统是一套具备实时管理能力,建立一个全面的、集成的、稳定的制造物流质量控制体系;对生产线、工艺、人员、品质、效率等多方位的监控、分析、改进,满足精细化、透明化、自动化、实时化、数据化、一体化管理,实现企业柔性化制造管理。
30 3
|
8天前
|
JavaScript 安全 Java
Spring Boot 和 Vue.js 实现的前后端分离的用户权限管理系统
Spring Boot 和 Vue.js 实现的前后端分离的用户权限管理系统
65 0
|
8天前
egg.js 24.14sequelize模型-修改和限制字段
egg.js 24.14sequelize模型-修改和限制字段
23 0
egg.js 24.14sequelize模型-修改和限制字段
|
8天前
|
JavaScript 前端开发 Java
jquery ajax+spring mvc上传文件
jquery ajax+spring mvc上传文件