odoo16前端框架源码阅读——rpc_service.js

odoo16前端框架源码阅读——rpc_service.js

先介绍点背景知识,这样方便阅读代码。

一、 JSONRPC的规范

https://www.jsonrpc.org/specification

中文翻译版本:https://wiki.geekdream.com/Specification/json-rpc_2.0.html

JSON-RPC是一个无状态且轻量级的远程过程调用(RPC)协议。 本规范主要定义了一些数据结构及其相关的处理规则。它允许运行在基于socket,http等诸多不同消息传输环境的同一进程中。其使用JSON(RFC 4627)作为数据格式。

它为简单而生!

由于JSON-RPC使用JSON,它具有与其相同的类型系统(见http://www.json.org或RFC 4627)。JSON可以表示四个基本类型(String、Numbers、Booleans和Null)和两个结构化类型(Objects和Arrays)。 规范中,术语“Primitive”标记那4种原始类型,“Structured”标记两种结构化类型。任何时候文档涉及JSON数据类型,第一个字母都必须大写:Object,Array,String,Number,Boolean,Null。包括True和False也要大写。

1、请求对象

发送一个请求对象至服务端代表一个rpc调用, 一个请求对象包含下列成员:

jsonrpc

指定JSON-RPC协议版本的字符串,必须准确写为“2.0”

method

包含所要调用方法名称的字符串,以rpc开头的方法名,用英文句号(U+002E or ASCII 46)连接的为预留给rpc内部的方法名及扩展名,且不能在其他地方使用。

params

调用方法所需要的结构化参数值,该成员参数可以被省略。

id

已建立客户端的唯一标识id,值必须包含一个字符串、数值或NULL空值。如果不包含该成员则被认定为是一个通知。该值一般不为NULL[1],若为数值则不应该包含小数[2]。

服务端必须回答相同的值如果包含在响应对象。 这个成员用来两个对象之间的关联上下文。

没有包含“id”成员的请求对象为通知, 作为通知的请求对象表明客户端对相应的响应对象并不感兴趣

    const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};

2、响应对象

当发起一个rpc调用时,除通知之外,服务端都必须回复响应。响应表示为一个JSON对象,使用以下成员:

jsonrpc

指定JSON-RPC协议版本的字符串,必须准确写为“2.0”

result

该成员在成功时必须包含。

当调用方法引起错误时必须不包含该成员。

服务端中的被调用方法决定了该成员的值。

error

该成员在失败是必须包含。

当没有引起错误的时必须不包含该成员。

该成员参数值必须为5.1中定义的对象。

id

该成员必须包含。

该成员值必须于请求对象中的id成员值一致。

若在检查请求对象id时错误(例如参数错误或无效请求),则该值必须为空值。

响应对象必须包含result或error成员,但两个成员必须不能同时包含。

3、错误对象

当一个rpc调用遇到错误时,返回的响应对象必须包含错误成员参数,并且为带有下列成员参数的对象:

code

使用数值表示该异常的错误类型。 必须为整数。

message

对该错误的简单描述字符串。 该描述应尽量限定在简短的一句话。

data

包含关于错误附加信息的基本类型或结构化类型。该成员可忽略。 该成员值由服务端定义(例如详细的错误信息,嵌套的错误等)。

code	message	meaning
-32700	Parse error语法解析错误	服务端接收到无效的json。该错误发送于服务器尝试解析json文本
-32600	Invalid Request无效请求	发送的json不是一个有效的请求对象。
-32601	Method not found找不到方法	该方法不存在或无效
-32602	Invalid params无效的参数	无效的方法参数。
-32603	Internal error内部错误	JSON-RPC内部错误。
-32000 to -32099	Server error服务端错误	预留用于自定义的服务器错误。

二、rpc_service.js

路径:addons\web\static\src\core\network\rpc_service.js

1、引入相关模块, 新建了相关的Error类

导入了两个js模块, browser估计是跟浏览器相关

registry 是前端的注册表

然后是定义了四个Error继承自己标准类Error

  • RPCError
  • ConnectionLostError
  • ConnectionAbortedError
  • HTTPError
/** @odoo-module **/import { browser } from "../browser/browser";
import { registry } from "../registry";// -----------------------------------------------------------------------------
// Errors
// -----------------------------------------------------------------------------
export class RPCError extends Error {constructor() {super(...arguments);this.name = "RPC_ERROR";this.type = "server";this.code = null;this.data = null;this.exceptionName = null;this.subType = null;}
}export class ConnectionLostError extends Error {}export class ConnectionAbortedError extends Error {}export class HTTPError extends Error {}

2、Error对象

根据响应值来返回一个RPCError,这句结构赋值挺有意思

const { code, data: errorData, message, type: subType } = reponse;

我猜是吧reponse中的4个属性 code,data,message,type 分别赋值给了code,errorData,message,subType,有两个变量名称做了替换。有啥必要吗?

// -----------------------------------------------------------------------------
// Main RPC method
// -----------------------------------------------------------------------------
export function makeErrorFromResponse(reponse) {// Odoo returns error like this, in a error field instead of properly// using http error codes...const error = new RPCError();error.exceptionName = errorData.name;error.subType = subType;error.data = errorData;error.message = message;error.code = code;return error;
}

3、定义相关变量

5个输入参数

  1. env 前端环境,似乎在前端js代码中,直接可以使用这个对象。
  2. rpcId id, 为了跟响应对应起来,所以需要这个id
  3. url 地址
  4. params 参数值
  5. setting 默认是一个空对象
export function jsonrpc(env, rpcId, url, params, settings = {}) {const bus = env.bus;const XHR = browser.XMLHttpRequest;const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};const request = settings.xhr || new XHR();let rejectFn;

参数看着有点多,其实在后面封装成service的时候, 前两个参数是不用传的,只需要传递后面三个就行, 其实params和setting都可以不用传,必须要传的只有url。

bus: 总线, 这里要通过总线发送一些信号,rpc毕竟是远程调用,难免有意外情况发生,所以需要bus进行通信。

XHR: 浏览器发起request请求

data: 标准的jsonrpc数据格式

rejectFn, 这里值得一提,在这里声明这个变量,但是并没有赋值,是为了后面使用。

4、复习promise

promise是ES6引入的异步编程的新解决方案,语法上Promise是一个构造函数,用来封装异步操作并可以获取成功或失败的结果。

// new Promise 生成一个异步任务,参数是具体执行任务的函数,接收两个参数
// 一般叫resolve和reject都是函数,异步任务执行成功调用前者,否则调用后者
// 这两个方法将改变p对象的状态,同时给下一步处理传递数据
// 然后调用p.then  ,接收两个函数型参数,分别对应异步任务成功的回调和失败的回调
const p = new Promise(function(resolve,reject){setTimeout(function () {// let data="数据库中的用户数据";// resolve(data);let err="数据读取失败";reject(err);},1000);
})p.then(function(value){console.log(value);
},function(reason){console.error(reason);
})// 其实promise解决的也是回调地狱,嵌套过多的问题,将异步任务封装成对象了

其实,promise分了两步来完成,对象本身只是执行了一个异步任务,并没有处理异步返回的结果。 异步任务返回的结果是在p.then函数中处理的。
异步任务的返回值会影响promise对象本身的状态,会决定p.then中执行哪个回调函数。

另外,就是promise可以链式调用,执行串行的异步任务。

5、promise 中的Bus

在promise里面往总线里发送了很多消息

        if (!settings.silent) {bus.trigger("RPC:REQUEST", data.id);}

这里既然发了,那么就一定有地方接收,在odoo中搜索一下RPC:REQUEST

addons\web\static\src\webclient\loading_indicator\loading_indicator.js

这个文件代码不长,直接贴过来好了。注释中大概的意思是:

加载指示器:

当用户执行一个动作,最好是给他一些反馈说明当前有些事情正在发生。 这个指示器的作用就是在屏幕的右下角显示一个小的矩形框,里面有Loading字样,并且还有rpc的id。 3秒之后,如果rpc依然没有完成,我们将阻塞整个UI。 回头测试一下。

/** @odoo-module **/import { browser } from "@web/core/browser/browser";
import { registry } from "@web/core/registry";
import { useService } from "@web/core/utils/hooks";
import { Transition } from "@web/core/transition";import { Component, onWillDestroy, useState } from "@odoo/owl";/*** Loading Indicator** When the user performs an action, it is good to give him some feedback that* something is currently happening.  The purpose of the Loading Indicator is to* display a small rectangle on the bottom right of the screen with just the* text 'Loading' and the number of currently running rpcs.** After a delay of 3s, if a rpc is still not completed, we also block the UI.*/
export class LoadingIndicator extends Component {setup() {this.uiService = useService("ui");this.state = useState({count: 0,show: false,});this.rpcIds = new Set();this.shouldUnblock = false;this.startShowTimer = null;this.blockUITimer = null;this.env.bus.addEventListener("RPC:REQUEST", this.requestCall.bind(this));this.env.bus.addEventListener("RPC:RESPONSE", this.responseCall.bind(this));onWillDestroy(() => {this.env.bus.removeEventListener("RPC:REQUEST", this.requestCall.bind(this));this.env.bus.removeEventListener("RPC:RESPONSE", this.responseCall.bind(this));});}requestCall({ detail: rpcId }) {if (this.state.count === 0) {browser.clearTimeout(this.startShowTimer);this.startShowTimer = browser.setTimeout(() => {if (this.state.count) {this.state.show = true;this.blockUITimer = browser.setTimeout(() => {this.shouldUnblock = true;this.uiService.block();}, 3000);}}, 250);}this.rpcIds.add(rpcId);this.state.count++;}responseCall({ detail: rpcId }) {this.rpcIds.delete(rpcId);this.state.count = this.rpcIds.size;if (this.state.count === 0) {browser.clearTimeout(this.startShowTimer);browser.clearTimeout(this.blockUITimer);this.state.show = false;if (this.shouldUnblock) {this.uiService.unblock();this.shouldUnblock = false;}}}
}LoadingIndicator.template = "web.LoadingIndicator";
LoadingIndicator.components = { Transition };registry.category("main_components").add("LoadingIndicator", {Component: LoadingIndicator,
});

6、request 绑定load事件

request绑定了一个load事件,也就是请求返回的时候触发的操作

        request.addEventListener("load", () => {if (request.status === 502) {// If Odoo is behind another server (eg.: nginx)if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());return;}let params;try {params = JSON.parse(request.response);} catch (_) {// the response isn't json parsable, which probably means that the rpc request could// not be handled by the server, e.g. PoolError('The Connection Pool Is Full')if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}return reject(new ConnectionLostError());const { error: responseError, result: responseResult } = params;if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (!responseError) {return resolve(responseResult);}const error = makeErrorFromResponse(responseError);reject(error);});

6.1、status = 502 是个什么鬼?

502 Bad Gateway错误是指代理或网关从上一个服务器接收到的响应无效或不完整。通常,这种情况发生在文件太大或处理速度太慢的高流量网站上。例如,当您访问一个具有高流量的网站时,您的请求将被发送到它的代理服务器。如果代理服务器在尝试访问网站时无法从上游服务器获取完整的响应,则会生成502错误代码。

502错误代码通常是由代理服务器、网关或负载均衡器等设备导致的,而不是由您的计算机或网络连接引起的。这意味着您只能为自己的网络连接做些有限的调整,但无法修复网关响应错误。

注释中也写的明白,502可能是因为使用了nginx反向代理,而 502错误是nginx和odoo通讯不佳造成的,这种情况rpc执行失败,

执行这一句

 reject(new ConnectionLostError());

6.2 解析返回值

如果返回的不是json格式的数据,也会触发错误

 try {params = JSON.parse(request.response);} catch (_) {return reject(new ConnectionLostError());}

catch 后面的这个下划线是什么鬼? 可能是并不关心发生了什么错误,只要解析错误,就调用reject

6.3 解构赋值

const { error: responseError, result: responseResult } = params;

根据jsonrpc规范, error和result 必须并且只能返回一个。

后面也做了判断

        if (!responseError) {return resolve(responseResult);}

如果没有错误,那就调用resolve,并返回。否则说明有错误发生,先生成一个error,然后调用reject

            const error = makeErrorFromResponse(responseError);reject(error);

7、request绑定 error事件

        request.addEventListener("error", () => {if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());});

8、 该干正事了

        request.open("POST", url);request.setRequestHeader("Content-Type", "application/json");request.send(JSON.stringify(data));

三行代码:

1、用post方法请求的url,为什么不用get? 因为post更安全

2、指定了Content-Type为json, 这个很重要,如果不指定,服务器端不知道怎么解析数据

3、将data转成字符串并发送出去。(忙活半天,就是为了这句)

9、定义了promise.abort

Promise只有三种状态:pending、resolve、reject,一个异步的承诺一旦发出,经历等待(pending)后,最终只能为成功或者失败,中途无法取消(abort)。

这里定义promise.abort,注释也讲的明白,允许用户取消被忽略的rpc请求来接触对UI的阻塞并且不要显示错误。

        /*** @param {Boolean} rejectError Returns an error if true. Allows you to cancel*                  ignored rpc's in order to unblock the ui and not display an error.*/promise.abort = function (rejectError = true) {if (request.abort) {request.abort();}if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (rejectError) {rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));}};

10、jsonrpc

这个函数就干了一件事,定义了一个promise对象来发送rpc请求,并把它返回。

export function jsonrpc(env, rpcId, url, params, settings = {}) {const bus = env.bus;const XHR = browser.XMLHttpRequest;const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};const request = settings.xhr || new XHR();let rejectFn;const promise = new Promise((resolve, reject) => {rejectFn = reject;// handle successrequest.addEventListener("load", () => {});// handle failurerequest.addEventListener("error", () => {});request.open("POST", url);request.setRequestHeader("Content-Type", "application/json");request.send(JSON.stringify(data));});promise.abort = function (rejectError = true) {};return promise;
}

11、定义RPC服务

这里对jsonrpc做了进一步封装,并且注册为服务,看来每个服务都有个start函数,而且将env作为参数传进去。

// -----------------------------------------------------------------------------
// RPC service
// -----------------------------------------------------------------------------
export const rpcService = {async: true,start(env) {let rpcId = 0;return function rpc(route, params = {}, settings) {return jsonrpc(env, rpcId++, route, params, settings);};},
};registry.category("services").add("rpc", rpcService);

附录: odoo16 rpc_service.js

/** @odoo-module **/import { browser } from "../browser/browser";
import { registry } from "../registry";// -----------------------------------------------------------------------------
// Errors
// -----------------------------------------------------------------------------
export class RPCError extends Error {constructor() {super(...arguments);this.name = "RPC_ERROR";this.type = "server";this.code = null;this.data = null;this.exceptionName = null;this.subType = null;}
}export class ConnectionLostError extends Error {}export class ConnectionAbortedError extends Error {}export class HTTPError extends Error {}// -----------------------------------------------------------------------------
// Main RPC method
// -----------------------------------------------------------------------------
export function makeErrorFromResponse(reponse) {// Odoo returns error like this, in a error field instead of properly// using http error codes...const { code, data: errorData, message, type: subType } = reponse;const error = new RPCError();error.exceptionName = errorData.name;error.subType = subType;error.data = errorData;error.message = message;error.code = code;return error;
}export function jsonrpc(env, rpcId, url, params, settings = {}) {const bus = env.bus;const XHR = browser.XMLHttpRequest;const data = {id: rpcId,jsonrpc: "2.0",method: "call",params: params,};const request = settings.xhr || new XHR();let rejectFn;const promise = new Promise((resolve, reject) => {rejectFn = reject;if (!settings.silent) {bus.trigger("RPC:REQUEST", data.id);}// handle successrequest.addEventListener("load", () => {if (request.status === 502) {// If Odoo is behind another server (eg.: nginx)if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());return;}let params;try {params = JSON.parse(request.response);} catch (_) {// the response isn't json parsable, which probably means that the rpc request could// not be handled by the server, e.g. PoolError('The Connection Pool Is Full')if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}return reject(new ConnectionLostError());}const { error: responseError, result: responseResult } = params;if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (!responseError) {return resolve(responseResult);}const error = makeErrorFromResponse(responseError);reject(error);});// handle failurerequest.addEventListener("error", () => {if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}reject(new ConnectionLostError());});// configure and send requestrequest.open("POST", url);request.setRequestHeader("Content-Type", "application/json");request.send(JSON.stringify(data));});/*** @param {Boolean} rejectError Returns an error if true. Allows you to cancel*                  ignored rpc's in order to unblock the ui and not display an error.*/promise.abort = function (rejectError = true) {if (request.abort) {request.abort();}if (!settings.silent) {bus.trigger("RPC:RESPONSE", data.id);}if (rejectError) {rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));}};return promise;
}// -----------------------------------------------------------------------------
// RPC service
// -----------------------------------------------------------------------------
export const rpcService = {async: true,start(env) {let rpcId = 0;return function rpc(route, params = {}, settings) {return jsonrpc(env, rpcId++, route, params, settings);};},
};registry.category("services").add("rpc", rpcService);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/140829.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用swagger-typescript-api

引言 前后端分离大致是这样的 后端:控制层 / 业务层 / 数据操作层前端:控制层 / 视图层 前后端的控制层,实际上就是前后端接口的对接 前后端分离,实现了更好地解耦合,但也引入了接口对接的过程,这个过程…

ChatGPT-3.5 插件推荐:语音输入,视频总结,联网检索

前言 GPT4 里是有内置的插件市场的,不过博主一直觉得自己对这个工具的使用还不够到位,现在购买升级版性价比不划算所以暂时还没有开。不过今天在学习使用的时候,发现 GPT3.5 也是可以通过网页插件方式进行升级扩展的,而且功能还比…

ChatGPT+Roblox,元宇宙的AI叙事逻辑#Leveling Up

MixCopilot 嗨,亲爱的听众朋友们!欢迎收听我们的播客节目!我是你们的主播:MixCopilot 混合副驾。今天我们要为大家带来的是我们的AI革命系列节目之一。这个系列节目聚焦于AI领域的一些最有影响力的建设者,他们将会讨论…

web网站 固定的邀请码字符 能被爬虫爬取吗?动态改变邀请码的字符是不是可以避免爬虫爬取或数据泄露

无论邀请码字符是固定的还是动态改变的,都无法完全避免爬虫爬取或数据泄露的风险。以下是一些要考虑的因素: 爬虫技术的发展:爬虫技术不断发展,可以智能地解析和获取网页内容。即使邀请码字符是固定的,高级爬虫仍然可以…

Linux - 驱动开发 - watchdog - SMP机制下多核确活

说明 理论上:不管IC是单核还是多核,只要watchdog有被循环feed,就不会触发超时重启,因此watchdog在SMP机制下的多核环境显得比较宽松,只要任意核存活(喂狗)就不会重启设备。 实际情况 有客户反…

直播间自动发言机器人的运行分享,与开发需要到的技术分析

先来看实操成果,↑↑需要的同学可看我名字↖↖↖↖↖,或评论888无偿分享 一、引言 随着人工智能技术的不断发展,自动发言机器人已经成为了当今社交媒体领域的重要组成部分。它们能够自动化地发布内容、回复用户评论和消息,大大提高…

【数据结构】栈和队列的模拟实现(两个方式实现)

前言 💓作者简介: 加油,旭杏,目前大二,正在学习C,数据结构等👀 💓作者主页:加油,旭杏的主页👀 ⏩本文收录在:再识C进阶的专栏&#x1…

Qt绘制简单图表

Qt图表类似于model/view,chart就是model。 创建图表的各个部件: QChart *chart new QChart();chart->setTitle(tr("简单函数曲线")); // chart->setAcceptHoverEvents(true);ui->chartView->setChart(chart);ui->chartVi…

网络安全自学

前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类,我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域,都有攻与防…

springboot---pom.xml

<?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 https://…

纯c语言模拟栈和队列(初学必看)

一、栈(Stack) 1.栈的概念及其结构 栈是一种特殊的线性表&#xff0c;在栈这个结构里&#xff0c;越先存进去的数据越难取出来。 这个结构就像是一个只有一端有打开的容器&#xff0c;越先放进去的球越在底部&#xff0c;想要把底部的球拿出来&#xff0c;就必须先把前面的求…

C#8.0本质论第十二章--泛型

C#8.0本质论第十二章–泛型 C#通过泛型来促进代码重用&#xff0c;在词义上等价于C模板。 在泛型编程中&#xff0c;数据类型也是一种参数。 12.1如果C#没有泛型 为object的方法使用值类型时&#xff0c;“运行时”将自动对它进行装箱&#xff0c;获取值类型的实例时则需要…

树莓派4B的测试记录(CPU、FFMPEG)

本文是用来记录树莓派 4B 的一些测试记录。 温度 下面记录中的风扇和大风扇是这样的&#xff1a; 为什么要用大风扇呢&#xff1f;因为小风扇在外壳上&#xff0c;气流通过外壳的珊格会有啸叫&#xff0c;声音不大但是很烦人&#xff0c;大风扇没这个问题&#xff0c;并且同样…

Vue3 数据响应式原理:Proxy和Reflect

我们在Vue2中使用的是Object.defineProperty方法来实现数据响应式的&#xff0c;可以通过get和set方法来监听对象的访问和修改。 但是并不能响应对象中属性的增加和删除&#xff0c;只能使用Vue.$set 和Vue.$delete 来对对象中的属性进行增加和删除。 数组也不能直接通过下标…

PyCharm因安装了illuminated Cloud插件导致加载项目失败

打开Pycharm时会有弹窗提示&#xff1a; The license for Illuminated Cloud is invalid or has expired. All Illuminated Cloud features will be disabled. 这个弹窗会导致你加载项目一直失败&#xff0c;close project 也关不掉&#xff0c;我都是用任务管理器杀死进程的…

Jmeter 性能 —— 负载阶梯场景!

1、安装阶梯测试的第三方插件->搜jpgc 选项-JMeter Plugins Manager -搜jpgc 空格&#xff0c;然后安装 2、脚本-线程组选jpgc Stepping Thread Group 最终并发数为100&#xff0c;并发数从0开始&#xff0c;5秒内增加10个并发数&#xff0c;增加10个后持续30s&#xff0c;…

学习c#的第十天

目录 C# 字符串&#xff08;String&#xff09; 创建 String 对象 String 类的属性 String 类的方法 实例 C#的string.Format格式化日期 C# 字符串&#xff08;String&#xff09; 创建 String 对象 可以使用以下方法之一来创建 string 对象&#xff1a; 1、通过给 St…

taro(踩坑) npm run dev:weapp 微信小程序开发者工具预览报错

控制台报错信息&#xff1a; VM72:9 app.js错误: Error: module vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js is not defined, require args is ./vendors-node_modules_taro_weapp_prebundle_chunk-JUEIR267_js.js 环境&#xff1a; node 版本&#x…

Spring Boot(二)

1、运行维护 1.1、打包程序 SpringBoot程序是基于Maven创建的&#xff0c;在Maven中提供有打包的指令&#xff0c;叫做package。本操作可以在Idea环境下执行。 mvn package 打包后会产生一个与工程名类似的jar文件&#xff0c;其名称是由模块名版本号.jar组成的。 1.2、程序…

vue分片上传视频并转换为m3u8文件并播放

开发环境&#xff1a; 基于若依开源框架的前后端分离版本的实践&#xff0c;后端java的springboot&#xff0c;前端若依的vue2&#xff0c;做一个分片上传视频并分段播放的功能&#xff0c;因为是小项目&#xff0c;并没有专门准备文件服务器和CDN服务&#xff0c;后端也是套用…