Texts

Vue项目中遇到了大文件分片上传的问题,之前用过webuploader,索性就把Vue2.0与webuploader结合起来使用,封装了一个vue的上传组件,使用起来也比较舒爽。

上传就上传吧,为什么搞得那么麻烦,用分片上传?

分片与并发结合,将一个大文件分割成多块,并发上传,极大地提高大文件的上传速度。
当网络问题导致传输错误时,只需要重传出错分片,而不是整个文件。另外分片传输能够更加实时的跟踪上传进度。

实现后的界面:

主要是两个文件,封装的上传组件和具体的ui页面,上传组件代码下面有列出来。这两个页面的代码放到github上了:http://github.com/shady-xia/vue-uploader-solutions/tree/master/vue-webuploader。

在项目中引入webuploader

  1. 先在系统中引入jquery(插件基于jq,坑爹啊!),如果你不知道放哪,那就放到index.html中。
  2. 在官网上下载Uploader.swf webuploader.min.js,可以放到项目静态目录static下面;在index.html中引入webuploader.min.js。
    (无需单独再引入webuploader.css,因为没有几行css,我们可以复制到vue组件中。)
<script src="/static/lib/jquery-2.2.3.min.js"></script>
<script src="/static/lib/webuploader/webuploader.min.js"></script>

需要注意的点:

  1. 在vue组件中,通过import './webuploader';的方式引入webuploader,会报''caller', 'callee', and 'arguments' properties may not be accessed on strict mode ...'的错, 这是因为你的babel使用了严格模式,而caller这些在严格模式下禁止使用。所以可以直接在index.html中引入webuploader.js,或者手动去解决babel中'use strict'的问题。

基于webuploader封装Vue组件

封装好的组件upload.vue如下,接口可以根据具体的业务进行扩展。

注意:功能和ui分离,此组建封装好了基本的功能,没有提供ui,ui在具体的页面上去实现。

<template>
    <div class="upload">
    </div>
</template>
<script>

    export default {
        name: 'vue-upload',
        props: {
            accept: {
                type: Object,
                default: null,
            },
            // 上传地址
            url: {
                type: String,
                default: '',
            },
            // 上传最大数量 默认为100
            fileNumLimit: {
                type: Number,
                default: 100,
            },
            // 大小限制 默认2M
            fileSingleSizeLimit: {
                type: Number,
                default: 2048000,
            },
            // 上传时传给后端的参数,一般为token,key等
            formData: {
                type: Object,
                default: null
            },
            // 生成formData中文件的key,下面只是个例子,具体哪种形式和后端商议
            keyGenerator: {
                type: Function,
                default(file) {
                    const currentTime = new Date().getTime();
                    const key = `${currentTime}.${file.name}`;
                    return key;
                },
            },
            multiple: {
                type: Boolean,
                default: false,
            },
            // 上传按钮ID
            uploadButton: {
                type: String,
                default: '',
            },
        },
        data() {
            return {
                uploader: null
            };
        },
        mounted() {
            this.initWebUpload();
        },
        methods: {
            initWebUpload() {

                this.uploader = WebUploader.create({
                    auto: true, // 选完文件后,是否自动上传
                    swf: '/static/lib/webuploader/Uploader.swf',  // swf文件路径
                    server: this.url,  // 文件接收服务端
                    pick: {
                        id: this.uploadButton,     // 选择文件的按钮
                        multiple: this.multiple,   // 是否多文件上传 默认false
                        label: '',
                    },
                    accept: this.getAccept(this.accept),  // 允许选择文件格式。
                    threads: 3,
                    fileNumLimit: this.fileNumLimit, // 限制上传个数
                    //fileSingleSizeLimit: this.fileSingleSizeLimit, // 限制单个上传图片的大小
                    formData: this.formData,  // 上传所需参数
                    chunked: true,          //分片上传
                    chunkSize: 2048000,    //分片大小
                    duplicate: true,  // 重复上传
                });

                // 当有文件被添加进队列的时候,添加到页面预览
                this.uploader.on('fileQueued', (file) => {
                    this.$emit('fileChange', file);
                });

                this.uploader.on('uploadStart', (file) => {
                    // 在这里可以准备好formData的数据
                    //this.uploader.options.formData.key = this.keyGenerator(file);
                });

                // 文件上传过程中创建进度条实时显示。
                this.uploader.on('uploadProgress', (file, percentage) => {
                    this.$emit('progress', file, percentage);
                });

                this.uploader.on('uploadSuccess', (file, response) => {
                    this.$emit('success', file, response);
                });

                this.uploader.on('uploadError', (file, reason) => {
                    console.error(reason);
                    this.$emit('uploadError', file, reason);
                });

                this.uploader.on('error', (type) => {
                    let errorMessage = '';
                    if (type === 'F_EXCEED_SIZE') {
                        errorMessage = `文件大小不能超过${this.fileSingleSizeLimit / (1024 * 1000)}M`;
                    } else if (type === 'Q_EXCEED_NUM_LIMIT') {
                        errorMessage = '文件上传已达到最大上限数';
                    } else {
                        errorMessage = `上传出错!请检查后重新上传!错误代码${type}`;
                    }

                    console.error(errorMessage);
                    this.$emit('error', errorMessage);
                });

                this.uploader.on('uploadComplete', (file, response) => {

                    this.$emit('complete', file, response);
                });
            },

            upload(file) {
                this.uploader.upload(file);
            },
            stop(file) {
                this.uploader.stop(file);
            },
            // 取消并中断文件上传
            cancelFile(file) {
                this.uploader.cancelFile(file);
            },
            // 在队列中移除文件
            removeFile(file, bool) {
                this.uploader.removeFile(file, bool);
            },

            getAccept(accept) {
                switch (accept) {
                    case 'text':
                        return {
                            title: 'Texts',
                            exteensions: 'doc,docx,xls,xlsx,ppt,pptx,pdf,txt',
                            mimeTypes: '.doc,docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt'
                        };
                        break;
                    case 'video':
                        return {
                            title: 'Videos',
                            exteensions: 'mp4',
                            mimeTypes: '.mp4'
                        };
                        break;
                    case 'image':
                        return {
                            title: 'Images',
                            exteensions: 'gif,jpg,jpeg,bmp,png',
                            mimeTypes: '.gif,.jpg,.jpeg,.bmp,.png'
                        };
                        break;
                    default: return accept
                }
            },

        },
    };
</script>
<style lang="scss">
// 直接把官方的css粘过来就行了
</style>

使用封装好的上传组件

新建页面,使用例子如下:

ui需要自己去实现。大概的代码可以点这里。

<vue-upload
        ref="uploader"
        url="xxxxxx"
        uploadButton="#filePicker"
        multiple
        @fileChange="fileChange"
        @progress="onProgress"
        @success="onSuccess"
></vue-upload>

分片的原理及流程

当我们上传一个大文件时,会被插件分片,ajax请求如下:

  1. 多个upload请求均为分片的请求,把大文件分成多个小份一次一次向服务器传递
  2. 分片完成后,即upload完成后,需要向服务器传递一个merge请求,让服务器将多个分片文件合成一个文件

分片

可以看到发起了多次upload的请求,我们来看看upload发送的具体参数:

第一个配置(content-disposition)中的guid和第二个配置中的access_token,是我们通过webuploader配置里的formData,即传递给服务器的参数
后面几个配置是文件内容,id、name、type、size等
其中chunks为总分片数,chunk为当前第几个分片。图片中分别为12和9。当你看到chunk是11的upload请求时,代表这是最后一个upload请求了。

合并

分片后,文件还未整合,数据大概是下面这个样子:

做完了分片后,其实工作还没完,我们还要再发送个ajax请求给服务器,告诉他把我们上传的几个分片合并成一个完整的文件。

我怎么知道分片上传完了,我在何时做合并?

webuploader插件有一个事件是uploadSuccess,包含两个参数,file和后台返回的response;当所有分片上传完毕,该事件会被触发,
我们可以通过服务器返回的字段来判断是否要做合并了。
比如后台返回了needMerge,我们看到它是true的时候,就可以发送合并的请求了。

存在的已知问题

在做单文件暂停与继续上传时,发现了这个插件的bug:

1、当设置的threads>1,使用单文件上传功能,即stop方法传入file时,会报错Uncaught TypeError: Cannot read property 'file' of undefined

出错的源码如下:这是因为暂停时为了让下一个文件继续传输,会将当前的pool池中pop掉暂停的文件流。这里做了循环,最后一次循环的时候,v是undefined的。

2、设置的threads为1,能正常暂停,但是暂停后再继续上传是失败的。

原理和上一个一样,暂停时把当前文件流在pool中全部pop了,当文件开始upload的时候,会检查当期pool,而此时已经没有之前暂停的文件流了。

如果是针对所有文件整体的暂停和继续,功能是正常的。
如果想实现单文件的暂停和继续功能,需要修改源码(我改了下源码,发现耦合度较高,工程量比想象的大,遂放弃)

后记

因为单文件暂停的bug,我最后放弃了这个插件,而且官方已经不再维护这个插件,github上issue成群,所以不太推荐大家用这个插件
后来我用vue-uploader(simple-uploader)无痛实现了文件分片上传、秒传及断点续传,大家想看的话我可以重新写一篇文章

这篇文章没有把一些知识点写全,其实思路是共通的:

1 在“加入文件”的回调中,通过FileReader读取文件,生成MD5,发给后台
2.1 如果后台直接返回了“跳过上传”字段和文件的url,则跳过上传,这是秒传;
2.2 如果后台返回了分片信息,这是断点续传。后台会在每个分片中标识这个分片是否上传过,你需要在分片上传校验的回调中判断,如果true则跳过该分片。
3 每个分片上传成功,后台都会返回一个字段判断是否需要合并;在“上传完成”的回调中,如果这个字段为true,则需要给后台发一个请求合并的ajax请求

本文转载于网络 如有侵权请联系删除

相关文章

  • el-Transfer 穿梭框官方案例

    原文:https://element-plus.gitee.io/zh-CN/component/transfer.html#%E5%9F%BA%E7%A1%80%E7%94%A8%E6%B3%95复制效果代码:<template> <pstyle="text-align:center;margin:0020px"> Customizedataitemsusingrender-content </p> <divstyle="text-align:center"> <el-transfer v-model="leftValue" style="text-align:left;display:inline-block" filterable :left-default-checked="[2,3]" :right-default-checked="[1]" :render-content="renderFun

  • JavaScript中的类型判断

    前言类型判断在web开发中有非常广泛的应用,简单的有判断数字还是字符串,进阶一点的有判断数组还是对象,再进阶一点的有判断日期、正则、错误类型,再再进阶一点还有比如判断plainObject、空对象、Window对象等等。typeof我们最最常用的莫过于typeof,注意,尽管我们会看到诸如:console.log(typeof('yayu'))//string的写法,但是typeof可是一个正宗的运算符,就跟加减乘除一样!这就能解释为什么下面这种写法也是可行的:console.log(typeof'yayu')//string引用《JavaScript权威指南》中对typeof的介绍:typeof是一元操作符,放在其单个操作数的前面,操作数可以是任意类型。返回值为表示操作数类型的一个字符串。那我们都知道,在ES6前,JavaScript共六种数据类型,分别是:Undefined、Null、Boolean、Number、String、Object然而当我们使用typeof对这些数据类型的值进行操作的时候,返回的结果却不是一一对应,分别是:undef

  • 大数据24小时 | 沈阳大数据研究院推出人才培养计划,美国思科赴华掘金中国大数据

    数据猿导读易联众拟3亿元参设人寿保险公司,布局医疗大数据领域;将算法植入网络零售,以色列公司Feelter获2000万美元融资;基于移动游戏的科技开发公司乐米科技新三板挂牌上市……以下为您奉上更多大数据热点事件来源:数据猿作者:abby一、数字媒体方案供应商Adobe拟5.4亿美元收购广告技术公司TubeMogul美国数字媒体和在线营销方案供应商Adobe近日发布公告称,公司将以每股14美元的价格收购广告技术公司TubeMogul全部股份,交易价格将达5.4亿美金。据了解,TubeMogul公司成立于2006年,是一个在线视频广告和分析平台,其分析服务可以提供视频浏览次数、视频观看者、视频观看者所处地理位置、视频观看时间等多项信息,从而帮助视频发行商进行综合决策。本次收购完成之后,Adobe旨在利用其广告技术优势,创建首个端到端的独立广告和数据管理解决方案。二、美国图形数据库公司NeoTechnology完成3600万美元融资 NeoTechnology公司总部位于美国加州,主要从图形数据库领域入手,为企业提供数据管理服务。其自主研发的产品Neo4j是一款高性能的图形数据库,可以帮助企

  • Java软件工程师基础阶段测试题

    Java基础|数据库|Android|学习视频|学习资料下载考试前言●回复"每日一练"获取以前的题目!●我希望大家积极参与答题!有什么不懂可以加小编微信进行讨论★珍惜每一天,拼搏每一天,专心每一天,成功每一天。考试主要有两种目的:一是检测考试者对某方面知识或技能的掌握程度;二是检验考试者是否已经具备获得某种资格的基本能力。(希望大家踊跃参与答题!读者可在右下角的留言留下你的答案!)

  • 如何生成「好」的图?面向图生成的深度生成模型系统综述|TPAMI2022

    【新智元导读】本文对用于图生成的深度生成模型领域的文献进行了广泛的概述。https://www.zhuanzhi.ai/paper/a904f0aa0762e65e1dd0b8b464df7168图是描述对象及其关系的重要数据表示形式,它们出现在各种各样的现实场景中。图生成是该领域的关键问题之一,它考虑的是学习给定图的分布,生成更多新的图。然而,由于其广泛的应用,具有丰富历史的图的生成模型传统上是手工制作的,并且只能对图的一些统计属性建模。最近在用于图生成的深度生成模型方面的进展是提高生成图的保真度的重要一步,并为新类型的应用铺平了道路。本文对用于图生成的深度生成模型领域的文献进行了广泛的概述。首先,给出了面向图生成的深度生成模型的形式化定义和初步知识;其次,分别提出了用于无条件和条件图生成的深度生成模型的分类;对各自已有的工作进行了比较分析。在此之后,将概述此特定领域中的评估指标。最后,总结了深度图生成的应用,并指出了五个有发展前景的研究方向。引言图在现实世界中无处不在,表示对象及其关系,如社会网络、引文网络、生物网络、交通网络等。众所周知,图还具有复杂的结构,其中包含丰富的底层值[

  • 使用C#编写一个.NET分析器(一)

    译者注 这是在Datadog公司任职的KevinGosse大佬使用C#编写.NET分析器的系列文章之一,在国内只有很少很少的人了解和研究.NET分析器,它常被用于APM(应用性能诊断)、IDE、诊断工具中,比如Datadog的APM,VisualStudio的分析器以及Rider和Reshaper等等。之前只能使用C++编写,自从.NETNativeAOT发布以后,使用C#编写变为可能。 笔者最近也在尝试开发一个运行时方法注入的工具,欢迎熟悉MSIL、PEMetadata布局、CLR源码、CLRProfilerAPI的大佬,或者对这个感兴趣的朋友留联系方式或者在公众号留言,一起交流学习。 原作者:KevinGosse 原文链接:https://minidump.net/writing-a-net-profiler-in-c-part-1-d3978aae9b12 项目链接:https://github.com/kevingosse/ManagedDotnetProfiler 简介 .NET具有非常强大的分析器API(ProfilerAPI,它类似于JavaAgent提供的API,但能做

  • 【划重点】医疗软件行业关键概念扫盲

    一、明确医疗行业关键概念 电子病历。电子病历(ElectronicMedicalRecord,EMR)是由医疗机构以电子化方式创建、保存和使用的,重点针对门诊、住院患者(或保健对象)临床诊疗和指导干预信息的数据集成系统,是居民个人在医疗机构历次就诊过程中产生和被记录的完整、详细的临床信息资源,是记录医疗诊治对象医疗服务活动记录的信息资源库,该信息资源库以计算机可处理的形式存在,并且能够安全的存储和传输,医院内授权用户可对其进行访问。 医院信息系统。医院信息系统(HospitalInformationSystem,HIS)是指利用计算机软硬件技术、网络通信技术等现代化手段,对医院及其所属各部门的人流、物流、财流进行综合管理,对在医疗活动各阶段产生的数据进行采集、储存、处理、提取、传输、汇总、加工生成各种信息,从而为医院的整体运行提供全面的、自动化的管理及各种服务的信息系统。 临床信息系统。临床信息系统(ClinicalInformationSystem,CIS)是医院信息系统的组成部分,其对在医疗活动各阶段产生的数据进行采集、储存、处理、提取、传输、汇总并加工生成各种信息,支持医院医护人

  • 接口测试常用测试点

    接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。 测试的策略: 接口测试也是属于功能测试,所以跟我们以往的功能测试流程并没有太大区别,测试流程依旧是: 评审测试接口文档(需求文档) 根据接口文档编写测试用例(用例编写完全可以按照以往规则来编写,例如等价类划分,边界值等设计方法) 执行测试,查看不同的参数请求,接口的返回的数据是否达到预期 那么设计测试用例时我们主要考虑如下几个方面: 功能测试: 接口的功能是否正确实现了 接口是否按照设计文档中来实现(比如username参数写为了user,那么这就不符合,因为接口文档在整个开发中都需要使用,所以接口实际的设计要与接口设计文档中保持一致) 兼容性测试:比如说今天接口进行了调整,但是前端没有进行变更,这时候需要验证新的接口是否满足旧的调用方式 错误码测试:通用的错误码与业务错误码是否能够清晰的说明调用问题,错误码是否能够尽可能的全的覆盖所有的情况 返回值测

  • servlet和CGI区别

    Servlet和CGI的区别Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。概括来讲,Servlet可以完成和CGI相同的功能。  CGI应用开发比较困难,因为它要求程序员有处理参数传递的知识,这不是一种通用的技能。CGI不可移植,为某一特定平台编写的CGI应用只能运行于这一环境中。每一个CGI应用存在于一个由客户端请求激活的进程中,并且在请求被服务后被卸载。这种模式将引起很高的内存、CPU开销,而且在同一进程中不能服务多个客户。  Servlet提供了Java应用程序的所有优势——可移植、稳健、易开发。使用ServletTag技术,Servlet能够生成嵌于静态HTML页面中的动态内容。  Ser

  • oracle学习总结

    一、SQL语言的发展SQL(StructureQueryLanguage)语言是数据库的核心语言。SQL的发展是从1974年开始的,其发展过程如下:1974年-----由Boyce和Chamberlin提出,当时称SEQUEL。1976年-----IBM公司的Sanjase研究所在研制RDBMSSYSTEMR时改为SQL。1979年-----ORACLE公司发表第一个基于SQL的商业化RDBMS产品。1982年-----IBM公司出版第一个RDBMS语言SQL/DS。1985年-----IBM公司出版第一个RDBMS语言DB2。1986年-----美国国家标准化组织ANSI宣布SQL作为数据库工业标准。SQL是一个标准的数据库语言,是面向集合的描述性非过程化语言。它功能强,效率高,简单易学易维护(迄今为止,我还没见过比它还好学的语言)。然而SQL语言由于以上优点,同时也出现了这样一个问题:它是非过程性语言,即大多数语句都是独立执行的,与上下文无关,而绝大部分应用都是一个完整的过程,显然用SQL完全实现这些功能是很困难的。所以大多数数据库公司为了解决此问题,作了如下两方面的工作:(1)扩

  • 新手如何正确开启页面与后台数据的交互之路

    如果你是一个新手! 如果你已经学会了如何运用html+css绘制出想要的页面! 如果你也基本学会了jQuery! 这个时候,你可以写页面之间的交互了! 那么初学者写页面交互要注意哪些事项呢? 下面我就跟初学者浅谈一下如何将所需要的数据录到后台数据库中,当然了,前提是后台开发者已给你写好了这样的一个接口,你去调用! 那么在调用接口之前,我们要做什么呢? 我们一定要做好调接口之前的一些逻辑判断。。。 如果你没有做好这个判断,后期会有很多bug丢给你,最重要的就是影响客户体验。 比方,你做一个登录接口,当你把用户名和密码传给后台,后台返回一条操作失败给你。这个时候,你不知道什么原因,然后后台人员去查看错误日志,发现你传了一个错误的用户名格式,这个时候,就是浪费了很多的时间了! 所以,我以自己的一些经验告诉一下大家,应该注意哪些东西?就先稍微说一下,关于添加数据应该需要注意的一些事。 1.如果是姓名,手机号,邮箱等等这些文本框,一定要做正则表达式判断! 2.如果是文本框,一定要做空数据判断(这里值得一提的事,很多新手,会将页面所有的数据都直接传到后台,如果是空的就传空数据,其实,如果是空的,这

  • ConcurrentHashMap源码解析,多线程扩容

    前面一篇已经介绍过了HashMap的源码: HashMap源码解析、jdk7和8之后的区别、相关问题分析 HashMap并不是线程安全的,他就一个普通的容器,没有做相关的同步处理,因此线程不安全主要体现在: put、get等等核心方法在多线程情况下,都会出现修改的覆盖,数据不一致等等问题。比如多个线程put先后的问题,会导致结果覆盖,如果一个put一个get,也可能会因为调度问题获取到错误的结果; 多线程操作有读有写的时候,可能会出现一个典型异常:ConcurrentModificationException 另外扩容的时候,hashmap1.7的实现还有可能出现死循环的问题。 关于线程安全的哈希映射的选择有三种: Hashtable; SynchronizedMap对HashMap包装; ConcurrentHashMap 其中,Hashtable的效率比较低,因为他的每一个方法都是用了锁,synchronized修饰的; 用SynchronizedMap对HashMap包装的实质也是额外加入一个对象叫做mutex,是一个Object,然后给对应的方法上都加上synchr

  • gitlab服务器文件上传及克隆,运作流程

    1.选定所用的仓库位置,鼠标右键点击gitbasehere(这部应该都会就不截图了)   2.初始化仓库(会在仓库中生成一个.git文件)   3.gitflow是一个插件(git的组合命令,初始化仓库并创建一个分支)后面的选项回车就行选择下分支    4.获取服务器上的代码 gitclonegit@域名:项目名/文件.git 5.在本地仓库中创建一个文件,随便写点什么   vimreadme.md   Thisisadevelop-test 这是我写的文件readme.md中的内容 6.gitadd. 7.commit-m"initcommit"(初始化添加) 8.remote-v 9.gitpush--all  

  • 英文短语/惯用语

    stumbleacross偶然遇到 itwouldbeeasierformetodosomething 做XXX更方便

  • hdu2029

    http://acm.hdu.edu.cn/showproblem.php?pid=2029 1#include<stdio.h> 2#include<string.h> 3#include<math.h> 4#include<stdlib.h> 5#include<iostream> 6usingnamespacestd; 7 8 9intmain() 10{ 11//freopen("in.txt","r",stdin); 12intn; 13chars[501]; 14 15while(cin>>n) 16{ 17while(n--) 18{ 19intflag=0; 20scanf("%s",s); 21intlen=strlen(s); 22for(inti=0,j=len-1;i<=j;i++,j--) 23{ 24if(s[i]!=s[j]) 25{ 26flag=1; 27break; 28} 29} 30if(flag==0) 31cout<<"yes"<<endl

  • [STM32F103]定时器中断

    l 使能定时器时钟。   RCC_APB1PeriphClockCmd(); l 初始化定时器,配置ARR,PSC。   TIM_TimeBaseInit(); l 开启定时器中断,配置NVIC。   voidTIM_ITConfig();   NVIC_Init(); l 使能定时器。   TIM_Cmd(); l 编写中断服务函数。   TIMx_IRQHandler();   l 源代码: 1//Tout(溢出时间)=(ARR+1)(PSC+1)/Tclk 2//500ms->(4999+1)(7199+1)/72000000=0.5s=500ms 3voidTIM3_Int_Init(u16arr,u16psc) 4{ 5TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure; 6NVIC_InitTypeDefNVIC_InitStructure; 7 8RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//时钟使

  • IDEA 如何升级Markdown的Mermaid的版本

    由于Mermaid升级过快,IDEA的插件版本更新不及时,自带的MarkDown插件体系多层引用关系,可能导致Mermaid版本过低,部分解析不到会报错: Syntaxerroringraph memaidversionx.x.x 此时的解决方案可以是手动升级mermaid版本 基础原理:对于memarid来说只是一个简单的js文件,本地使用npmimemard安装获取最新的memard的js文件,然后替换js文件到idea指定目录,重新打开md文件即可实现更新版本,记住备份旧版本memard.js文件 操作过程: 由于mermaid项目经常发版,所以最好使用npm获取最新版本,在命令行下输入npmimemard,安装完成后在node_modules中找到memard.js文件,拷贝到IDEA的指定目录:JetBrains\IdeaIC2022.2\markdown\download\mermaidlanguageextension\mermaid 不同的版本会有差异,可以使用everything搜索mermaid.js文件去搜索,找到目录,备份该文件,然后将npm生成的文件拷贝到以上

  • Hibernate Criteria条件Restrictions.or查询 循环多个or条件

    Hibernate条件or查询循环多个or条件 今天公司让我做一个列表查询页面,因为还是对于hibernate的不熟悉刚出来经验不足,代码敲得少,所有导致了种种困难…页面左边有个多级菜单栏,因为种种需求需要我用到SQL查询条件里面的or(或者),但因为用的是hibernate所以我苦苦在网上搜寻,得到了如下代码: 1Criteriacriteria=this.createCriteria(); 2criteria.add(Restrictions.or(Restrictions.eq("name","a"),Restrictions.eq("name","b")));复制 但是这样只能是一次写一个or条件,我的需求需要我写类似name=”a”orname=”b”orname=”c”…… 所有我又继续搜索,得到了如下代码: 1Criteriacriteria=this.createCriteria(); 2Disjunctiondis=Restrictions.disjunction(); 3for(inti=0;i<5;i++){ 4dis.add(Restric

  • 贪婪算法-货物装载问题

    贪婪算法-货物装载问题 最优解问题限制条件--------满足条件=可行解优化函数--------满足限制=最优解贪心准则:选择的依据或者标准货物装载拓扑排序有向图得遍历最小生成树 1//changethelengthofanarray 2 3//changeLength1D.h 4 5#ifndefchangeLength1D_ 6#definechangeLength1D_ 7 8#include"myExceptions.h" 9 10usingnamespacestd; 11 12template<classT> 13voidchangeLength1D(T*&a,intoldLength,intnewLength) 14{ 15if(newLength<0) 16throwillegalParameterValue("newlengthmustbe>=0"); 17 18T*temp=newT[newLength];//newarray 19intnumber=oldLength>newLength?newLength:oldLen

  • Git 基本命令

    1.Gitclone+地址——仓库保存到本地 2.Gitadd.——上传 3.Gitcommit-m'注释' 4.Gitpush——上传到服务器 5.Gitcheckout-b'分支名' 6.Gitcheckout分支名——切换分支 7.Gitpushorigin分支名——上传到分支 8.Gitmerge分支名——同步到主分支 9.Gitpulloriginmaster——在子分支中拉取主分支

  • for循环:用turtle画一颗五角星

    importturtle turtle.forward(100) turtle.right(144) turtle.forward(100) turtle.right(144) turtle.forward(100) turtle.right(144) turtle.forward(100) turtle.right(144) turtle.forward(100) turtle.right(144) turtle.exitonclick()复制 importturtle foriinrange(5): turtle.forward(100) turtle.right(144) turtle.exitonclick()复制 importturtle #设置初始位置 turtle.penup() turtle.left(90) turtle.fd(200) turtle.pendown() turtle.right(90) #花蕊 turtle.fillcolor("red") turtle.begin_fill() turtle.circle(10

相关推荐

推荐阅读