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失败。当然跳转不同的页面需要不同的跳转数据,我们可以在组件方法参数上进行添加。不需要的在调用时赋值空就行了。

目录
相关文章
|
24天前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的客户关系管理系统附带文章源码部署视频讲解等
85 2
|
16天前
|
JavaScript Java PHP
快速对比:Django、Spring Boot、Node.js 和 PHP
快速对比:Django、Spring Boot、Node.js 和 PHP
46 7
|
5天前
|
关系型数据库 MySQL Java
SpringBoot项目中mysql字段映射使用JSONObject和JSONArray类型
SpringBoot项目中mysql字段映射使用JSONObject和JSONArray类型
9 0
|
22天前
|
安全 算法 Java
强大!基于Spring Boot 3.3 六种策略识别上传文件类型
【10月更文挑战第1天】在Web开发中,文件上传是一个常见的功能需求。然而,如何确保上传的文件类型符合预期,防止恶意文件入侵,是开发者必须面对的挑战。本文将围绕“基于Spring Boot 3.3 六种策略识别上传文件类型”这一主题,分享一些工作学习中的技术干货,帮助大家提升文件上传的安全性和效率。
36 0
|
2月前
|
Java API 开发者
【已解决】Spring Cloud Feign 上传文件,提示:the request was rejected because no multipart boundary was found的问题
【已解决】Spring Cloud Feign 上传文件,提示:the request was rejected because no multipart boundary was found的问题
263 0
|
3月前
|
Java 开发工具 Spring
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
【Azure Spring Cloud】使用azure-spring-boot-starter-storage来上传文件报错: java.net.UnknownHostException: xxxxxxxx.blob.core.windows.net: Name or service not known
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的小区物流配送系统附带文章源码部署视频讲解等
109 4
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp的宠物援助平台附带文章源码部署视频讲解等
78 4
|
4月前
|
JavaScript Java 测试技术
基于springboot+vue.js+uniapp宿舍管理系统的附带文章源码部署视频讲解等
基于springboot+vue.js+uniapp宿舍管理系统的附带文章源码部署视频讲解等
80 3