当前位置: 首页 > news >正文

java中使用svnkit实现文件的版本管理

java中使用svnkit实现文件的版本管理

    • 一、引入svnKit依赖
    • 二、初始化仓库工厂类
    • 二、使用svnkit创建本地存储仓库
    • 三、svn基本原子操作
    • 四、通过原子方法实现简单svn相应操作

一、引入svnKit依赖

        <dependency><groupId>org.tmatesoft.svnkit</groupId><artifactId>svnkit</artifactId><version>1.9.3</version></dependency>

二、初始化仓库工厂类

本文参考来自于svnkit官网
在使用svn客户端进行各种操作时第一步需要初始化仓库工厂

/** Initializes the library to work with a repository via* different protocols.*/private static void setupLibrary() {/** For using over http:// and https://*/DAVRepositoryFactory.setup();/** For using over svn:// and svn+xxx://*/SVNRepositoryFactoryImpl.setup();/** For using over file:///*/FSRepositoryFactory.setup();}

二、使用svnkit创建本地存储仓库

try {String tgtPath = "d:/modelFiles/repos";SVNURL tgtURL = SVNRepositoryFactory.createLocalRepository( new File( tgtPath ), true , false );} catch ( SVNException e ) {//handle exception}

执行完这一段代码之后,我们可以在本地目录下发现以下目录结构
在这里插入图片描述

三、svn基本原子操作

    private static SVNClientManager ourClientManager;private static ISVNEventHandler myCommitEventHandler;private static ISVNEventHandler myUpdateEventHandler;private static ISVNEventHandler myWCEventHandler;/** Puts directories and files under version control scheduling them for addition* to a repository. They will be added in a next commit. Like 'svn add PATH'* command. It's done by invoking** SVNWCClient.doAdd(File path, boolean force,* boolean mkdir, boolean climbUnversionedParents, boolean recursive)** which takes the following parameters:** path - an entry to be scheduled for addition;** force - set to true to force an addition of an entry anyway;** mkdir - if true doAdd(..) creates an empty directory at path and schedules* it for addition, like 'svn mkdir PATH' command;** climbUnversionedParents - if true and the parent of the entry to be scheduled* for addition is not under version control, then doAdd(..) automatically schedules* the parent for addition, too;** recursive - if true and an entry is a directory then doAdd(..) recursively* schedules all its inner dir entries for addition as well.*/// 将文件纳入版本控制private static void addEntry(File wcPath) throws SVNException {ourClientManager.getWCClient().doAdd(wcPath, false, false, false, true);}/** Updates a working copy (brings changes from the repository into the working copy).* Like 'svn update PATH' command; It's done by invoking** SVNUpdateClient.doUpdate(File file, SVNRevision revision, boolean recursive)** which takes the following parameters:** file - a working copy entry that is to be updated;** revision - a revision to which a working copy is to be updated;** recursive - if true and an entry is a directory then doUpdate(..) recursively* updates the entire directory, otherwise - only child entries of the directory;*/// 将本地文件更新到指定版本,前提是本地有该文件的其他版本private static long update(File wcPath,SVNRevision updateToRevision, boolean isRecursive)throws SVNException {SVNUpdateClient updateClient = ourClientManager.getUpdateClient();/** sets externals not to be ignored during the update*/updateClient.setIgnoreExternals(false);/** returns the number of the revision wcPath was updated to*/return updateClient.doUpdate(wcPath, updateToRevision, isRecursive);}/** Checks out a working copy from a repository. Like 'svn checkout URL[@REV] PATH (-r..)'* command; It's done by invoking** SVNUpdateClient.doCheckout(SVNURL url, File dstPath, SVNRevision pegRevision,* SVNRevision revision, boolean recursive)** which takes the following parameters:** url - a repository location from where a working copy is to be checked out;** dstPath - a local path where the working copy will be fetched into;** pegRevision - an SVNRevision representing a revision to concretize* url (what exactly URL a user means and is sure of being the URL he needs); in other* words that is the revision in which the URL is first looked up;** revision - a revision at which a working copy being checked out is to be;** recursive - if true and url corresponds to a directory then doCheckout(..) recursively* fetches out the entire directory, otherwise - only child entries of the directory;*/// svn检出操作,即从svn仓库拉取指定版本的代码private static long checkout(SVNURL url,SVNRevision revision, File destPath, boolean isRecursive)throws SVNException {SVNUpdateClient updateClient = ourClientManager.getUpdateClient();/** sets externals not to be ignored during the checkout*/updateClient.setIgnoreExternals(false);/** returns the number of the revision at which the working copy is*/return updateClient.doCheckout(url, destPath, revision, revision, isRecursive);}/** Committs changes in a working copy to a repository. Like* 'svn commit PATH -m "some comment"' command. It's done by invoking** SVNCommitClient.doCommit(File[] paths, boolean keepLocks, String commitMessage,* boolean force, boolean recursive)** which takes the following parameters:** paths - working copy paths which changes are to be committed;** keepLocks - if true then doCommit(..) won't unlock locked paths; otherwise they will* be unlocked after a successful commit;** commitMessage - a commit log message;** force - if true then a non-recursive commit will be forced anyway;** recursive - if true and a path corresponds to a directory then doCommit(..) recursively* commits changes for the entire directory, otherwise - only for child entries of the* directory;*/// svn提交操作,将本地文件提交到svn仓库private static SVNCommitInfo commit(File wcPath, boolean keepLocks, String commitMessage)throws SVNException {/** Returns SVNCommitInfo containing information on the new revision committed* (revision number, etc.)*/return ourClientManager.getCommitClient().doCommit(new File[] { wcPath }, keepLocks,commitMessage, false, true);}/** Imports an unversioned directory into a repository location denoted by a* destination URL (all necessary parent non-existent paths will be created* automatically). This operation commits the repository to a new revision.* Like 'svn import PATH URL (-N) -m "some comment"' command. It's done by* invoking** SVNCommitClient.doImport(File path, SVNURL dstURL, String commitMessage, boolean recursive)** which takes the following parameters:** path - a local unversioned directory or singal file that will be imported into a* repository;** dstURL - a repository location where the local unversioned directory/file will be* imported into; this URL path may contain non-existent parent paths that will be* created by the repository server;** commitMessage - a commit log message since the new directory/file are immediately* created in the repository;** recursive - if true and path parameter corresponds to a directory then the directory* will be added with all its child subdirictories, otherwise the operation will cover* only the directory itself (only those files which are located in the directory).*/// 本地目录初始提交到svn仓库private static SVNCommitInfo importDirectory(File localPath, SVNURL dstURL, String commitMessage, boolean isRecursive) throws SVNException{/** Returns SVNCommitInfo containing information on the new revision committed* (revision number, etc.)*/return ourClientManager.getCommitClient().doImport(localPath, dstURL, commitMessage, isRecursive);}/** Schedules directories and files for deletion from version control upon the next* commit (locally). Like 'svn delete PATH' command. It's done by invoking** SVNWCClient.doDelete(File path, boolean force, boolean dryRun)** which takes the following parameters:** path - an entry to be scheduled for deletion;** force - a boolean flag which is set to true to force a deletion even if an entry* has local modifications;** dryRun - set to true not to delete an entry but to check if it can be deleted;* if false - then it's a deletion itself.*/// svn删除操作private static void delete(File wcPath, boolean force) throws SVNException {ourClientManager.getWCClient().doDelete(wcPath, force, false);}// svn删除整个工作目录操作public static SVNCommitInfo deleteDir(ISVNEditor editor , String dirPath ) throws SVNException {editor.openRoot( -1 );editor.deleteEntry( dirPath , -1 );//Closes the root directory.editor.closeDir( );return editor.closeEdit( );}// 创建指定版本分支private static SVNCommitInfo copy(SVNURL srcURL, SVNURL dstURL,SVNRevision revision,boolean isMove, String commitMessage) throws SVNException {/** SVNRevision.HEAD means the latest revision.* Returns SVNCommitInfo containing information on the new revision committed* (revision number, etc.)*/SVNCopySource[] sources = {new SVNCopySource(null, revision, srcURL)};return ourClientManager.getCopyClient().doCopy(sources , dstURL, false, false, true, commitMessage, null);}/** Called recursively to obtain all entries that make up the repository tree* repository - an SVNRepository which interface is used to carry out the* request, in this case it's a request to get all entries in the directory* located at the path parameter;* * path is a directory path relative to the repository location path (that* is a part of the URL used to create an SVNRepository instance);*  */// 打印指定版本下的文件列表信息public static void listEntries(SVNRepository repository, String path, Long version)throws SVNException {/** Gets the contents of the directory specified by path at the latest* revision (for this purpose -1 is used here as the revision number to* mean HEAD-revision) getDir returns a Collection of SVNDirEntry* elements. SVNDirEntry represents information about the directory* entry. Here this information is used to get the entry name, the name* of the person who last changed this entry, the number of the revision* when it was last changed and the entry type to determine whether it's* a directory or a file. If it's a directory listEntries steps into a* next recursion to display the contents of this directory. The third* parameter of getDir is null and means that a user is not interested* in directory properties. The fourth one is null, too - the user* doesn't provide its own Collection instance and uses the one returned* by getDir.*/Collection entries = repository.getDir(path, version, null,(Collection) null);Iterator iterator = entries.iterator();while (iterator.hasNext()) {SVNDirEntry entry = (SVNDirEntry) iterator.next();System.out.println("/" + (path.equals("") ? "" : path + "/")+ entry.getName() + " (author: '" + entry.getAuthor()+ "'; revision: " + entry.getRevision() + "; date: " + entry.getDate() + ")");/** Checking up if the entry is a directory.*/if (entry.getKind() == SVNNodeKind.DIR) {listEntries(repository, (path.equals("")) ? entry.getName(): path + "/" + entry.getName());}}}

四、通过原子方法实现简单svn相应操作

	// svn 工作目录private static String copyPath = "C:/modelFiles/copy/";// svn 仓库目录private static String reposPath= "C:/modelFiles/repos/";// 临时文件存放目录private static String tmpPath= "C:/modelFiles/tmp/";// 初始提交本地copyPath下文件夹到svn仓库public static long commitFile(String dirName) throws SVNException {File sourcePath = new File(copyPath+dirName);SVNURL targetURL = SVNURL.parseURIEncoded("file:///"+reposPath+dirName);committedRevision = importDirectory(sourcePath, targetURL, "上传模型文件夹 "+dirName, true).getNewRevision();return committedRevision;}// 检出指定版本到指定目录// 其中 SVNRevision 为版本对象,可以通过SVNRevision.create(1) 形式创建版本id为1的版本对象// svn版本从1开始往后迭代,为长整型,0表示最新版本用 SVNRevision.HEAD表示,其等同于 SVNRevision.create(0)public static void checkOut(String dirName,String basePath,SVNRevision svnRevision) throws SVNException {SVNURL repository = SVNURL.parseURIEncoded("file:///"+reposPath);File wcDir = new File(basePath+"/"+dirName);checkout(repository.appendPath(dirName,false), svnRevision, wcDir, true);}// svn工作目录下新增文件夹或文件后提交至svn仓库public static long commitNewFile(String dirName,String newDir) throws SVNException {// svn工作区目录File wcDir = new File(copyPath+dirName);// svn工作目录下新增的文件夹File NewDir = new File(copyPath+newDir);// 为了防止当前不是最新版本先从svn仓库更新下最新代码,其实也可以不进行这一步操作update(wcDir, SVNRevision.HEAD, true);// 将当前新增的目录加入版本控制addEntry(NewDir);// 提交至svn仓库long newRevision = commit(wcDir, false,"在 " + dirName + " 目录下新增 " + newDir).getNewRevision();log.info("在 "+dirName+" 目录下新增 "+newDir);return newRevision;}// 删除svn工作目录下某个文件夹并提交svn仓库public static long deleteFile(String dirName,String delDir) throws SVNException {File wcDir = new File(copyPath+dirName);File DelDir = new File(copyPath+delDir);delete(DelDir, false);long newRevision = commit(wcDir, false,"从 " + dirName + " 目录下删除 " + delDir).getNewRevision();log.info("从 "+dirName+" 目录下删除 "+delDir);//删除后更新下update(wcDir, SVNRevision.HEAD, true);return newRevision;}// svn删除整个工作目录操作public static void deleteFileFromRepos(String dirName){try {SVNRepository svnRepository = FSRepositoryFactory.create(SVNURL.parseURIEncoded("file:///" + reposPath));ISVNEditor editor = svnRepository.getCommitEditor( "删除目录 "+dirName , null );try {deleteDir(editor, dirName);} catch ( SVNException svne ) {editor.abortEdit( );throw svne;}} catch (Exception e) {e.printStackTrace();}}/*** @description 创建指定版本的分支*/public static Long createBranchByCopy(String dirName,Long revisionNum) {try {SVNURL srcURL = SVNURL.parseURIEncoded("file:///"+reposPath+dirName);SVNURL dstURL = SVNURL.parseURIEncoded("file:///"+reposPath+dirName+"-分支");SVNCommitInfo copyInfo = copy(srcURL,dstURL,SVNRevision.create(revisionNum),true," 创建分支:" + dirName+"-分支");checkOut(getBranchName(dirName+"-分支",copyPath);return copyInfo.getNewRevision();} catch (SVNException e) {log.info("创建分支失败:"+e.getMessage());throw new RuntimeException(e);}}// 获取当前模型仓库的最新版本编号public static long getLatestRevision() throws SVNException {SVNRepository svnRepository = FSRepositoryFactory.create(SVNURL.parseURIEncoded("file:///" + reposPath));return svnRepository.getLatestRevision();}

相关文章:

java中使用svnkit实现文件的版本管理

java中使用svnkit实现文件的版本管理 一、引入svnKit依赖二、初始化仓库工厂类二、使用svnkit创建本地存储仓库三、svn基本原子操作四、通过原子方法实现简单svn相应操作 一、引入svnKit依赖 <dependency><groupId>org.tmatesoft.svnkit</groupId><artifa…...

了解 Linux 网络卡绑定:提高网络性能与冗余性

在现代 IT 基础设施中&#xff0c;网络性能和可靠性至关重要。对于许多企业和个人用户来说&#xff0c;确保网络的高可用性和冗余性是首要任务之一。Linux 提供了一个强大的解决方案——网络卡绑定&#xff08;Network Interface Card Bonding&#xff0c;简称 NIC Bonding&…...

2024年618购物狂欢节即将来袭!精选五款超值入手数码好物!

618购物狂欢盛宴即将落幕&#xff0c;是时候展现我们的购物智慧了&#xff01;在追求价格优惠的同时&#xff0c;我们更应看重商品的品质与实用性。面对琳琅满目的选择&#xff0c;如何筛选出真正值得拥有的好物呢&#xff1f;为了让大家的购物之旅更加轻松愉快&#xff0c;以下…...

中国AI独角兽资本大冒险

成立不过一年多时间&#xff0c;月之暗面已然成为中国大模型赛道上&#xff0c;最炙手可热的明星公司。 5月21日&#xff0c;华尔街见闻获悉&#xff0c;月之暗面将按照投前估值30亿美元&#xff08;合217.3亿人民币&#xff09;进行融资&#xff0c;完成后依然会是当前中国估…...

项目十二:简单的python基础爬虫训练

许久未见&#xff0c;甚是想念&#xff0c;今日好运&#xff0c;为你带好运。ok&#xff0c;废话不多说&#xff0c;希望这门案例能带你直接快速了解并运用。&#x1f381;&#x1f496; 基础流程 第一步&#xff1a;安装需要用到的requests库&#xff0c;命令如下 pip inst…...

OpenGL学习入门及开发环境搭建

最近学习OpenGL开发&#xff0c;被各种openGL库搞得晕头转向&#xff0c;什么glut, glew glfw glad等等。 可以参考这边博客:OpenGL 下面的 glut freeglut glfw 都是个啥_glx wgl的中文-CSDN博客 glfw是glut的升级版&#xff0c;跨平台的主要处理窗口 事件相关。 glad是glew…...

three.js能实现啥效果?看过来,这里都是它的菜(08)

在Three.js中实现旋转动画的原理是通过修改对象的旋转属性来实现的&#xff0c;通常使用渲染循环&#xff08;render loop&#xff09;来更新对象的旋转状态&#xff0c;从而实现动画效果。 具体的原理包括以下几个步骤&#xff1a; 创建对象&#xff1a;首先创建一个需要旋转…...

SpringBoot(九)之整合mybatis

SpringBoot&#xff08;九&#xff09;之整合mybatis 文章目录 SpringBoot&#xff08;九&#xff09;之整合mybatisSpring整合mybatis回顾1. 引入依赖2. mybatis-config.xml SpringBoot整合mybatis1.引入依赖2. 配置数据源和 MyBatis 属性3. 配置 Mapper 接口4. 配置mapper.xm…...

【实战教程】使用Spring AOP和自定义注解监控接口调用

一、背景 随着项目的长期运行和迭代&#xff0c;积累的功能日益繁多&#xff0c;但并非所有功能都能得到用户的频繁使用或实际上根本无人问津。 为了提高系统性能和代码质量&#xff0c;我们往往需要对那些不常用的功能进行下线处理。 那么&#xff0c;该下线哪些功能呢&…...

算法学习之:Raft-分布式一致性/共识算法

基础介绍 Raft是什么&#xff1f; Raft is a consensus algorithm that is designed to be easy to understand. Its equivalent to Paxos in fault-tolerance and performance. The difference is that its decomposed into relatively independent subproblems, and it clea…...

彩色进度条(C语言版本)

.h文件 #include<stdio.h> #include<windows.h>#define NUM 101 #define LOAD_UP 50 #define LOAD_DOWN 60 #define SLEEP_SLOW 300 #define SLEEP_FAST 70 版本1&#xff1a;&#xff08;初始版&#xff09; //v1 #include "progress.h" int main() …...

C#和C++有什么区别?

C#和C都是广泛使用的编程语言&#xff0c;但它们在设计理念、应用场景和语法上有许多显著的区别。以下是一些关键区别的详细介绍&#xff1a; 1. 设计理念和目的 C&#xff1a; 设计目的&#xff1a;C是一种面向系统编程和应用程序开发的语言&#xff0c;具有高效性和灵活性…...

微信小程序报错:notifyBLECharacteristicValueChange:fail:nodescriptor的解决办法

文章目录 一、发现问题二、分析问题二、解决问题 一、发现问题 微信小程序报错&#xff1a;notifyBLECharacteristicValueChange:fail:nodescriptor 二、分析问题 这个提示有点问题&#xff0c;应该是该Characteristic的Descriptor有问题&#xff0c;而不能说nodescriptor。 …...

富格林:可信攻略阻止遭遇欺诈

富格林悉知&#xff0c;在投资市场中&#xff0c;如何阻止遭遇欺诈情况应该是每位投资者都想要了解的一个知识点。事实上&#xff0c;现货黄金市场相对来说会其他市场复杂多变&#xff0c;因此要想盈利出金还是得要先学会阻止遭遇欺诈情况。据富格林所知&#xff0c;目前市面上…...

搭建淘宝扭蛋机小程序:技术选型与最佳实践

随着移动互联网的快速发展&#xff0c;小程序作为一种轻量级应用&#xff0c;以其无需安装、即用即走的特点&#xff0c;受到了广大用户的喜爱。在电商领域&#xff0c;淘宝作为国内最大的电商平台之一&#xff0c;也积极拥抱小程序技术&#xff0c;为用户提供更加便捷、个性化…...

【线性回归】梯度下降

文章目录 [toc]数据数据集实际值估计值 梯度下降算法估计误差代价函数学习率参数更新 Python实现导包数据预处理迭代过程结果可视化完整代码 结果可视化线性拟合结果代价变化 数据 数据集 ( x ( i ) , y ( i ) ) , i 1 , 2 , ⋯ , m \left(x^{(i)} , y^{(i)}\right) , i 1 ,…...

GMSL图像采集卡,适用于无人车、自动驾驶、自主机器、数据采集等场景,支持定制

基于各种 系列二代 G MS L 图像采集卡&#xff08;以下简称 二代图像采集卡&#xff09;是一款自主研发的一款基于 F P G A 的高速图像产品&#xff0c;二代图像采集卡相比一代卡&#xff0c;由于采用PCIe G en 3 技术&#xff0c;速度和带宽都相应的有了成 倍的提高。该图像…...

docker不删除容器更改其挂载目录

场景&#xff1a;docker搭建的jenkins通常需要配置很多开发环境&#xff0c;当要更换挂载目录&#xff0c;每次都需要删除容器重新运行&#xff0c;不在挂载目录的环境通常不会保留。 先给一个参考博客docker不删除容器&#xff0c;修改容器挂载或其他_jenkins 修改容器挂载do…...

K8s Service 背后是怎么工作的?

kube-proxy 是 Kubernetes 集群中负责服务发现和负载均衡的组件之一。它是一个网络代理&#xff0c;运行在每个节点上, 用于 service 资源的负载均衡。它有两种模式&#xff1a;iptables 和 ipvs。 iptables iptables 是 Linux 系统中的一个用户空间实用程序&#xff0c;用于…...

ClickHouse配置与使用

静态IP配置 # 修改网卡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33# 修改文件内容 TYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic IPADDR192.168.18.128 NETMASK255.255.255.0 GATEWAY192.168.18.2 DEFROUTEyes IPV4_FAILURE_FATALno IPV6INIT…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

leetcode73-矩阵置零

leetcode 73 思路 记录 0 元素的位置&#xff1a;遍历整个矩阵&#xff0c;找出所有值为 0 的元素&#xff0c;并将它们的坐标记录在数组zeroPosition中置零操作&#xff1a;遍历记录的所有 0 元素位置&#xff0c;将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...

java 局域网 rtsp 取流 WebSocket 推送到前端显示 低延迟

众所周知 摄像头取流推流显示前端延迟大 传统方法是服务器取摄像头的rtsp流 然后客户端连服务器 中转多了&#xff0c;延迟一定不小。 假设相机没有专网 公网 1相机自带推流 直接推送到云服务器 然后客户端拉去 2相机只有rtsp &#xff0c;边缘服务器拉流推送到云服务器 …...

Java高级 |【实验八】springboot 使用Websocket

隶属文章&#xff1a;Java高级 | &#xff08;二十二&#xff09;Java常用类库-CSDN博客 系列文章&#xff1a;Java高级 | 【实验一】Springboot安装及测试 |最新-CSDN博客 Java高级 | 【实验二】Springboot 控制器类相关注解知识-CSDN博客 Java高级 | 【实验三】Springboot 静…...