Java 阿里云视频直播开发流程
首先来看一下直播效果 推流工具有很多种(例如OBS、阿里云直播Demo推流、等等,我用的是芯象导播)阿里播放器地址
一、直播基础服务概述 官方文档说明
二、直播域名配置需要两个域名(推流域名、播流域名) 官方文档说明
四、开发流程
1、Java SDK安装 我用的是Apache Maven安装方式 官方安装说明文档
2、Java代码生成推流地址和播放地址 官方案例
package com.zaiyun.zhibo.utils;import com.zaiyun.common.core.domain.model.User;
import com.zaiyun.common.utils.DateUtils;
import com.zaiyun.common.utils.SecurityUtils;
import com.zaiyun.zhibo.domain.LiveRooms;
import org.springframework.stereotype.Component;import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.HashMap;/*** 阿里视频直播工具类*/
@Component
public class LiveUtils {/*** 生成推流地址** @param appName 推流AppName* @param streamName 推流StreamName*/public static String buildPushUrl(String appName, String streamName) {String pushDomain = "zyt.*****.com"; //推流域名String pushKey = "2T39rSP2dVro";//推流域名配置的鉴权KeyString pushUrl = "";//推流域名未开启鉴权功能的情况下if (pushKey == "") {pushUrl = "rtmp://" + pushDomain + "/" + appName + "/" + streamName;} else {Long timeStamp = System.currentTimeMillis() / 1000L + 3601L;String stringToMd5 = "/" + appName + "/" + streamName + "-" + Long.toString(timeStamp) + "-0-0-" + pushKey;String authKey = md5(stringToMd5);pushUrl = "rtmp://" + pushDomain + "/" + appName + "/" + streamName + "?auth_key=" + Long.toString(timeStamp) + "-0-0-" + authKey;}return pushUrl;}/*** 生成播放地址** @param appName 播放appName(同推流appName)* @param streamName 播放streamName,播放源流时,streamName 同推流streamName;播放转码流时,streamName 为推流streamName_{转码模板ID}*/public static HashMap<String, String> buildPullUrl(String appName, String streamName) {String pullDomain = "zyb.*****.com"; //播放域名String pullKey = "92KiuYjNYr5H"; //播放鉴权KeyString rtmpUrl = ""; //rtmp的拉流地址String hlsUrl = ""; //m3u8的拉流地址String flvUrl = ""; //flv的拉流地址String rtsUrl = ""; //rts的拉流地址//播放域名未配置鉴权Key的情况下if (pullKey == "") {rtmpUrl = "rtmp://" + pullDomain + "/" + appName + "/" + streamName;rtsUrl = "artc://" + pullDomain + "/" + appName + "/" + streamName;hlsUrl = "http://" + pullDomain + "/" + appName + "/" + streamName + ".m3u8";flvUrl = "http://" + pullDomain + "/" + appName + "/" + streamName + ".flv";} else {Long timeStamp = System.currentTimeMillis() / 1000L + 3600L;String rtmpToMd5 = "/" + appName + "/" + streamName + "-" + Long.toString(timeStamp) + "-0-0-" + pullKey;String rtmpAuthKey = md5(rtmpToMd5);rtmpUrl = "rtmp://" + pullDomain + "/" + appName + "/" + streamName + "?auth_key=" + Long.toString(timeStamp) + "-0-0-" + rtmpAuthKey;String hlsToMd5 = "/" + appName + "/" + streamName + ".m3u8-" + Long.toString(timeStamp) + "-0-0-" + pullKey;String hlsAuthKey = md5(hlsToMd5);hlsUrl = "http://" + pullDomain + "/" + appName + "/" + streamName + ".m3u8" + "?auth_key=" + Long.toString(timeStamp) + "-0-0-" + hlsAuthKey;String flvToMd5 = "/" + appName + "/" + streamName + ".flv-" + Long.toString(timeStamp) + "-0-0-" + pullKey;String flvAuthKey = md5(flvToMd5);flvUrl = "http://" + pullDomain + "/" + appName + "/" + streamName + ".flv" + "?auth_key=" + Long.toString(timeStamp) + "-0-0-" + flvAuthKey;String rtsToMd5 = "/" + appName + "/" + streamName + "-" + Long.toString(timeStamp) + "-0-0-" + pullKey;String rtsAuthKey = md5(rtsToMd5);rtsUrl = "artc://" + pullDomain + "/" + appName + "/" + streamName + "?auth_key=" + Long.toString(timeStamp) + "-0-0-" + rtsAuthKey;}HashMap<String, String> url = new HashMap<>();url.put("rtmpUrl", rtmpUrl);url.put("hlsUrl", hlsUrl);url.put("flvUrl", flvUrl);url.put("rtsUrl", rtsUrl);return url;}/*** 计算md5*/public static String md5(String param) {if (param == null || param.length() == 0) {return null;}try {MessageDigest md5 = MessageDigest.getInstance("MD5");md5.update(param.getBytes());byte[] byteArray = md5.digest();BigInteger bigInt = new BigInteger(1, byteArray);// 参数16表示16进制String result = bigInt.toString(16);// 不足32位高位补零while (result.length() < 32) {result = "0" + result;}return result;} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return null;}/*** 登录需的要鉴权信息*/public static HashMap<String, Object> appAuth(LiveRooms room) {String role = "";User user = SecurityUtils.getLoginUser().getUser();String userId = user.getUserId().toString();String appId = room.getAppId();String appKey = room.getAppKey();String appSign = room.getAppSign();String nonce = java.util.UUID.randomUUID().toString();Long timestamp = DateUtils.addHours(new Date(), 1).getTime() / 1000;String signContent = String.format("%s%s%s%s%s%s", appId, appKey, userId, nonce, timestamp, role);String appToken = org.apache.commons.codec.digest.DigestUtils.sha256Hex(signContent);HashMap<String, Object> result = new HashMap<>();result.put("appId", appId);result.put("appSign", appSign);result.put("appToken", appToken);result.put("role", role);result.put("nonce", nonce);result.put("userId", userId);result.put("timestamp", timestamp);result.put("userName", user.getUserName());return result;}
}
3、直播间互动功能(消息组)
package com.zaiyun.zhibo.controller;import com.zaiyun.common.annotation.Anonymous;
import com.zaiyun.common.core.controller.BaseController;
import com.zaiyun.common.core.domain.AjaxResult;
import com.zaiyun.zhibo.domain.LiveRooms;
import com.zaiyun.zhibo.mapper.LiveRoomsMapper;
import com.zaiyun.zhibo.utils.LiveAppUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.HashMap;/*** 最新版本的消息组*/
@Anonymous
@RestController
@RequestMapping("/live/app")
public class LiveAppController extends BaseController {@ResourceLiveAppUtils liveAppUtils;@ResourceLiveRoomsMapper liveRoomsMapper;/*** 创建互动消息应用*/@PostMapping("/create")public AjaxResult createMessageApp() {try {String appName = "myApp";return success(liveAppUtils.createMessageApp(appName));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 获取互动消息应用*/@PostMapping("/list")public AjaxResult getMessageAppList() {try {return success(liveAppUtils.getMessageAppList());} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 获取互动消息应用详情*/@PostMapping("/info")public AjaxResult getMessageAppInfo() {try {String appId = "abc13a82b773";return success(liveAppUtils.getMessageAppInfo(appId));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 创建消息组*/@PostMapping("/group/create")public AjaxResult createMessageGroup() {try {String appId = "abc13a82b773";return success(liveAppUtils.createMessageGroup(appId));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 查询指定用户下消息组列表*/@PostMapping("/group/list")public AjaxResult listMessageGroup() {try {String appId = "abc13a82b773";return success(liveAppUtils.listMessageGroup(appId));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 消息组详情*/@PostMapping("/group/describe")public AjaxResult groupDescribe() {try {HashMap<String, String> parameter = new HashMap<>();parameter.put("appId", "abc13a82b773");parameter.put("groupId", "211a39c8-2ebd-4d4b-978e-dfa31836220f");return success(liveAppUtils.groupDescribe(parameter));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 发送消息到群组*/@PostMapping("/send/group")public AjaxResult sendMessageGroup(@RequestBody HashMap<String, String> data) {try {String roomId = data.get("roomId");String msgType = data.get("msgType");String body = data.get("body");if (roomId == null || msgType == null || body == null) {return AjaxResult.error(201, "参数错误 roomId 或 msgType 或 body");}LiveRooms liveRooms = new LiveRooms();liveRooms.setId(Integer.parseInt(roomId));LiveRooms room = liveRoomsMapper.findRoomByWhere(liveRooms);HashMap<String, String> parameter = new HashMap<>();parameter.put("appId", room.getAppId());parameter.put("groupId", room.getGroupId());parameter.put("senderId", "admin" + getUserId());//发送者parameter.put("msgType", msgType);//消息类型parameter.put("body", body);//消息内容return success(liveAppUtils.sendMessageGroup(parameter));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 发送消息到用户*/@PostMapping("/send/user")public AjaxResult sendMessageUser() {try {HashMap<String, String> parameter = new HashMap<>();parameter.put("appId", "abc13a82b773");parameter.put("senderId", "uid10");//发送者parameter.put("receiverId", "100");//接收者parameter.put("body", "{\"content\":\"这里是发送的消息内容!\"}");//消息内容return success(liveAppUtils.sendMessageUser(parameter));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 查询群组消息列表*/@PostMapping("/group/messages")public AjaxResult getGroupMessages() {try {HashMap<String, String> parameter = new HashMap<>();parameter.put("appId", "abc13a82b773");parameter.put("groupId", "211a39c8-2ebd-4d4b-978e-dfa31836220f");return success(liveAppUtils.getGroupMessages(parameter));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}/*** 查询群组用户列表*/@PostMapping("/group/users")public AjaxResult getGroupUsers() {try {HashMap<String, String> parameter = new HashMap<>();parameter.put("appId", "abc13a82b773");parameter.put("groupId", "211a39c8-2ebd-4d4b-978e-dfa31836220f");return success(liveAppUtils.getGroupUsers(parameter));} catch (Exception e) {return AjaxResult.error(201, e.getMessage());}}
}
package com.zaiyun.zhibo.utils;import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.live.model.v20161101.*;
import com.aliyuncs.profile.DefaultProfile;
import com.zaiyun.common.config.AlibabaConfig;
import com.zaiyun.common.utils.uuid.UUID;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.HashMap;/*** 阿里视频直播工具类*/
@Component
public class LiveAppUtils {/*** 初始化配置*/public static IAcsClient createClient() throws Exception {DefaultProfile profile = DefaultProfile.getProfile("cn-shanghai", AlibabaConfig.getAccessKeyId(), AlibabaConfig.getAccessKeySecret());return new DefaultAcsClient(profile);}/*** 创建互动消息应用** @param appName 互动消息应用名称,长度 2~16 个字符*/public static CreateLiveMessageAppResponse createMessageApp(String appName) throws Exception {IAcsClient client = createClient();CreateLiveMessageAppRequest request = new CreateLiveMessageAppRequest();request.setAppName(appName);request.setAuditType(1);//内置安全审核try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("创建互动消息应用异常!" + error);}}/*** 查询互动消息应用列表*/public static Object getMessageAppList() throws Exception {IAcsClient client = createClient();ListLiveMessageAppsRequest request = new ListLiveMessageAppsRequest();request.setSortType(1);try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("查询互动消息应用列表异常!" + error);}}/*** 查询互动消息应用详情*/public static Object getMessageAppInfo(String appId) throws Exception {IAcsClient client = createClient();DescribeLiveMessageAppRequest request = new DescribeLiveMessageAppRequest();request.setAppId(appId);try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("查询互动消息应用详情异常!" + error);}}/*** 创建消息组** @param appId 互动消息应用ID*/public static String createMessageGroup(String appId) throws Exception {IAcsClient client = createClient();CreateLiveMessageGroupRequest request = new CreateLiveMessageGroupRequest();request.setAppId(appId);request.setCreatorId("admin1");//群组创建者ID。request.setDataCenter("cn-shanghai");//数据中心request.setGroupInfo("testgroupinfo");//群组扩展信息,最大512字符。request.setGroupId(UUID.fastUUID().toString());//要创建的群组ID,由大小写字母、数字组成,最大64字符。request.setGroupName(RandomStringUtils.randomAlphanumeric(10));//群组名,最大64字符。ArrayList<String> administrators = new ArrayList<>();administrators.add("aaaaa");administrators.add("bbbbb");administrators.add("ccccc");request.setAdministrators(administrators);//管理员用户ID数组try {CreateLiveMessageGroupResponse response = client.getAcsResponse(request);return response.getGroupId();} catch (ClientException error) {throw new RuntimeException("创建消息组异常!" + error);}}/*** 查询指定用户下消息组列表** @param appId 互动消息应用ID*/public static Object listMessageGroup(String appId) throws Exception {IAcsClient client = createClient();ListLiveMessageGroupsRequest request = new ListLiveMessageGroupsRequest();request.setAppId(appId);request.setSortType(2);try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("查询指定用户下消息组列表异常!" + error);}}/*** 消息组详情** @param parameter 请求参数*/public static Object groupDescribe(HashMap<String, String> parameter) throws Exception {IAcsClient client = createClient();DescribeLiveMessageGroupRequest request = new DescribeLiveMessageGroupRequest();request.setAppId(parameter.get("appId"));request.setGroupId(parameter.get("groupId"));try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("查询指定用户下消息组列表异常!" + error);}}/*** 发送消息到群组** @param parameter 请求参数*/public static Object sendMessageGroup(HashMap<String, String> parameter) throws Exception {IAcsClient client = createClient();SendLiveMessageGroupRequest request = new SendLiveMessageGroupRequest();request.setAppId(parameter.get("appId"));request.setGroupId(parameter.get("groupId"));request.setSenderId(parameter.get("senderId"));//发送者request.setBody(parameter.get("body"));//消息内容request.setMsgType(Long.parseLong(parameter.get("msgType")));//消息类型try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("发送消息到群组异常!" + error);}}/*** 发送消息到用户** @param parameter 请求参数*/public static Object sendMessageUser(HashMap<String, String> parameter) throws Exception {IAcsClient client = createClient();SendLiveMessageUserRequest request = new SendLiveMessageUserRequest();request.setAppId(parameter.get("appId"));request.setSenderId(parameter.get("senderId"));//发送者request.setReceiverId(parameter.get("receiverId"));//接收者request.setBody(parameter.get("body"));//消息内容try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("发送消息到用户异常!" + error);}}/*** 查询群组消息列表** @param parameter 请求参数*/public static Object getGroupMessages(HashMap<String, String> parameter) throws Exception {IAcsClient client = createClient();ListLiveMessageGroupMessagesRequest request = new ListLiveMessageGroupMessagesRequest();request.setAppId(parameter.get("appId"));request.setGroupId(parameter.get("groupId"));request.setSortType(2);request.setPageSize(10);try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("查询群组消息列表异常!" + error);}}/*** 查询群组用户列表** @param parameter 必须参数*/public static Object getGroupUsers(HashMap<String, String> parameter) throws Exception {IAcsClient client = createClient();ListLiveMessageGroupUsersRequest request = new ListLiveMessageGroupUsersRequest();request.setAppId(parameter.get("appId"));request.setGroupId(parameter.get("groupId"));request.setSortType(2);request.setPageSize(30);try {return client.getAcsResponse(request);} catch (ClientException error) {throw new RuntimeException("查询群组用户列表异常!" + error);}}
}
4、推流、断流回调
/*** 推流、断流回调地址*/@GetMapping("/callback")public void callback(HttpServletRequest request) {try {//处理回调请求参数HashMap<String, Object> paramsMap = ConvertUtils.getRequestData(request);//开始推流if (paramsMap.get("action").equals("publish")) {tiktokLogger.info("开始推流:" + paramsMap);liveRoomsLogService.addRoomLog(paramsMap);}//推流中断if (paramsMap.get("action").equals("publish_done")) {tiktokLogger.info("推流中断:" + paramsMap);liveRoomsLogService.upRoomLog(paramsMap);}} catch (Exception e) {tiktokLogger.info("回调异常:" + e.getMessage());}}/*** 处理请求参数** @param request 请求* @return 结果*/public static HashMap<String, Object> getRequestData(HttpServletRequest request) {HashMap<String, Object> paramsMap = new HashMap<>(); //重新定义请求的参数Map<String, String[]> map = request.getParameterMap(); //请求中的map数组for (String key : map.keySet()) { //遍历数组String[] value = map.get(key);if (value.length == 1) {paramsMap.put(key, map.get(key)[0]);} else {paramsMap.put(key, value);}}return paramsMap;}
推流、断流日志
09:44:32.770 [http-nio-8082-exec-13] INFO extend-tiktok - [callback,44] - 开始推流:{app=zyt.***.com, node=117.49.93.167, appname=TtSCpj, width=1080, action=publish, id=D6v3Su5mMQ, time=1723599872, usrargs=vhost=zyb.***.com&auth_key=1723603295-0-0-487e7adb13e00bdda7e7c6cbd97f88f8&ali_publisher&ali_edge_node_ip=117.49.93.167&ali_node_via=live13.cn4435%2clive15.l2et135-3&ali_node_ip=10.120.24.162#26%2c118.178.204.227#6&alilive_streamidv2=live13.cn4435_2955_3699959155_1723599872186&alilive_clienthost=live15.l2et135-3&orig_tc_url=rtmp://zyt.***.com/TtSCpj, height=1920}
09:46:06.204 [http-nio-8082-exec-15] INFO extend-tiktok - [callback,50] - 推流中断:{app=zyt.***.com, node=117.49.93.167, appname=TtSCpj, width=1080, action=publish_done, id=D6v3Su5mMQ, time=1723599966, usrargs=vhost=zyb.***.com&auth_key=1723603295-0-0-487e7adb13e00bdda7e7c6cbd97f88f8&ali_publisher_ip=&ali_edge_node_ip=117.49.93.167&ali_node_via=live13.cn4435%2clive15.l2et135-3&ali_node_ip=10.120.24.162#26%2c118.178.204.227#6&alilive_streamidv2=live13.cn4435_2955_3699959155_1723599872186&alilive_clienthost=live15.l2et135-3&orig_tc_url=rtmp://zyt.***.com/TtSCpj, height=1920}
09:46:25.407 [http-nio-8082-exec-64] INFO extend-tiktok - [callback,44] - 开始推流:{app=zyt.***.com, node=117.49.93.167, appname=Srvv66, ip=, width=1080, action=publish, id=b974e1hqmy, time=1723599985, usrargs=vhost=zyb.***.com&auth_key=1723603558-0-0-c54c7bad6bde755300bac0efb5ae44b7&ali_publisher_ip=&ali_edge_node_ip=117.49.93.167&ali_node_via=live6.cn4435%2clive3.l2et135-3&ali_node_ip=10.120.24.217#26%2c118.178.204.215#6&alilive_streamidv2=live6.cn4435_3485_3698551537_1723599984805&alilive_clienthost=live3.l2et135-3&orig_tc_url=rtmp://zyt.***.com/Srvv66, height=1920}
09:53:58.001 [http-nio-8082-exec-53] INFO extend-tiktok - [callback,50] - 推流中断:{app=zyt.***.com, node=117.49.93.167, appname=Srvv66, ip=, width=1080, action=publish_done, id=b974e1hqmy, time=1723600437, usrargs=vhost=zyb.***.com&auth_key=1723603558-0-0-c54c7bad6bde755300bac0efb5ae44b7&ali_publisher_ip=&ali_edge_node_ip=117.49.93.167&ali_node_via=live6.cn4435%2clive3.l2et135-3&ali_node_ip=10.120.24.217#26%2c118.178.204.215#6&alilive_streamidv2=live6.cn4435_3485_3698551537_1723599984805&alilive_clienthost=live3.l2et135-3&orig_tc_url=rtmp://zyt.***.com/Srvv66, height=1920}
11:01:52.914 [http-nio-8082-exec-18] INFO extend-tiktok - [callback,44] - 开始推流:{app=zyt.***.com, node=58.222.29.233, appname=Srvv66, ip=, width=1080, action=publish, id=b974e1hqmy, time=1723604512, usrargs=vhost=zyb.***.com&auth_key=1723603558-0-0-c54c7bad6bde755300bac0efb5ae44b7&ali_publisher_ip=&ali_edge_node_ip=58.222.29.233&ali_node_via=live1.cn3421%2clive15.l2et135-3&ali_node_ip=10.120.25.6#26%2c118.178.204.227#6&alilive_streamidv2=live1.cn3421_76677_2481419939_1723604511502&alilive_clienthost=live15.l2et135-3&orig_tc_url=rtmp://zyt.***.com/Srvv66, height=1920}
11:14:48.320 [http-nio-8082-exec-65] INFO extend-tiktok - [callback,50] - 推流中断:{app=zyt.***.com, node=58.222.29.233, appname=Srvv66, ip=, width=1080, action=publish_done, id=b974e1hqmy, time=1723605288, usrargs=vhost=zyb.***.com&auth_key=1723603558-0-0-c54c7bad6bde755300bac0efb5ae44b7&ali_publisher_ip=&ali_edge_node_ip=58.222.29.233&ali_node_via=live1.cn3421%2clive15.l2et135-3&ali_node_ip=10.120.25.6#26%2c118.178.204.227#6&alilive_streamidv2=live1.cn3421_76677_2481419939_1723604511502&alilive_clienthost=live15.l2et135-3&orig_tc_url=rtmp://zyt.***.com/Srvv66, height=1920}
直播记录
相关文章:

Java 阿里云视频直播开发流程
首先来看一下直播效果 推流工具有很多种(例如OBS、阿里云直播Demo推流、等等,我用的是芯象导播)阿里播放器地址 一、直播基础服务概述 官方文档说明 二、直播域名配置需要两个域名(推流域名、播流域名) 官方文档说…...
SQLite 轻量级的嵌入式关系型数据库的替代软件
SQLite 是一个轻量级的嵌入式关系型数据库,由于其简单易用和跨平台的特性,被广泛应用于各种应用程序中。以下是一些可作为SQLite替代品的数据库软件或可视化管理工具: 1. **SQLiteStudio**:这是一个免费、开源的跨平台SQLite数据…...

Flutter-自适用高度PageView
需求 在 Flutter 中,PageView 是一个非常常用的组件,能够实现多个页面的滑动切换。然而,默认的 PageView 高度是固定的,这在展示不同高度的页面时,可能会导致不必要的空白或内容裁剪问题。为了使 PageView 能够根据每…...

群晖NAS本地搭建可远程交互的大型语言模型LLM聊天机器人
文章目录 前言1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址 前言 本文主要分享如何在群晖NAS本地部署并运行一个基于大语言模型Llama 2的个人本地聊天机器人并结合内网穿透工具…...
TypeScript 构建工具之 webpack
在实际开发中,直接使用TypeScript 编译器的情况不多。 在项目中,需要使用构建工具对代码进行打包,不可能脱离项目使用TypeScript 编译器单独打包TypeScript 。 那如何将 webpack 和 TypeScript 进行集成? 参考文档: w…...

conda环境下在pycharm中调试scrapy项目
前提条件 已经创建好了conda环境已经安装好了scrapy框架项目初始化完成 编写一个爬虫脚本 import scrapyclass StackOverflowSpider(scrapy.Spider):name stackoverflowstart_urls [http://stackoverflow.com/questions?sortvotes]def parse(self, response):print("…...
contenteditable=“true“的标签限制字数的时候修改光标位置
contenteditable"true"的标签限制字数的时候修改光标位置 有时候input和textarea并不能完全满足ui需求,这个时候我们就用contenteditable"true"来将别的标签修改为可编辑状态,但当我们通过js修改了内容之后光标的位置就是一个问题&…...

51单片机-LED灯蜂鸣器数码管按键DS18B20温度传感器
LDE灯的相关程序 LED灯闪烁 LED流水灯 方法1 方法二: 因为P1口可以直接控制P1^0~P1^7的8个led灯,利用一个8位的二进制数字来进行控制即可。如果要点亮P1^0 只需要给P1口传递 1111 1110即可。 蜂鸣器的使用 什么是蜂鸣器? 蜂鸣器是一种一…...
笔记本一线品牌有哪些
笔记本电脑的一线品牌通常指的是在市场上具有较高市场份额、良好口碑、较强的技术实力和服务能力的品牌。根据目前的信息,笔记本电脑市场的一线品牌主要包括以下几个: 联想 (Lenovo):联想在全球笔记本市场上的占有率较高,其产品线…...

mysql聚合函数和分组
我最近开了几个专栏,诚信互三! > |||《算法专栏》::刷题教程来自网站《代码随想录》。||| > |||《C专栏》::记录我学习C的经历,看完你一定会有收获。||| > |||《Linux专栏》࿱…...
ubuntu20.04+RealSenseD455
ubuntu20.04安装驱动双目相机RealSenseD455 安装环境安装RealSense SDK 2.0ROS包安装启动Realsense摄像头存在的 bugD455标定安装环境 系统:Ubuntu20.04 ROS:Noetic 视觉传感器:Intel RealSense D455 安装RealSense SDK 2.0 该安装有两种方式,一个是用命令安装,另一个是…...
WAF绕过技巧
WAF绕过技巧 WAF(Web Application Firewall)是一种安全系统,旨在监控和控制网络流量,以防止攻击,如SQL 注入、跨站脚本(XSS)和拒绝服务(DoS)。 WAF 可以通过多种方式绕过…...

HarmonyOS应用三之组件生命周期和参数传递
目录: 1、生命周期的执行顺序2、页面数据传递3、图片的读取4、数据的备份和恢复5、轮播图6、页面布局图 1、生命周期的执行顺序 /** Copyright (c) 2023 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* yo…...
[Qt][Qt 网络][上]详细讲解
目录 0.概述1.UDP Socket1.核心API概览2.回显服务器3.回显客户端 0.概述 要使用Qt中有关网络编程的API,需要添加network模块 1.UDP Socket 1.核心API概览 主要的类有两个:QUdpSocket和QNetworkDatagramQUdpSocket表⽰⼀个UDP的socket⽂件 bind(const …...

读零信任网络:在不可信网络中构建安全系统21读后总结与感想兼导读
1. 基本信息 零信任网络:在不可信网络中构建安全系统 道格巴斯(Doug Barth) 著 人民邮电出版社,2019年8月出版 1.1. 读薄率 书籍总字数252千字,笔记总字数73194字。 读薄率73194252000≈29.5% 这个读薄率是最高的吧&#x…...

Java基础——注释
在开发中注释是必不可少的,帮助我们更好的标记阅读代码,下面介绍几种常用的注释方式。 一、注释种类 1. 单行注释 使用//一行代码来进行注释,只能注释一行内容 2. 多行注释 使用斜杠星号的方式 /*注释多行代码*/,注释多行代…...

Redis未授权访问漏洞利用合集
一、基本信息 靶机:IP:192.168.100.40 攻击机:IP:192.168.100.60 二、漏洞 & 过程 Redis 未授权访问漏洞利用无口令远程登录靶机 靶机 cd redis-4.0.8/src./redis-server ../redis.conf 攻击机 ./redis-cli -h 192.168.100.40 Redis 未授权访问…...

基于asp.net的在线考试系统、基于c#的在线考试管理系统
摘 要 伴随着社会以及科学技术的发展,互联网已经渗透在人们的身边,网络慢慢的变成了人们的生活必不可少的一部分,紧接着网络飞速的发展,管理系统这一名词已不陌生,越来越多的学校、公司等机构都会定制一款属于自己个…...

将 hugo 博客搬迁到服务器
1. 说明 在 Ubuntu 22.04 上使用 root 账号,创建普通账号,并赋予 root 权限。 演示站点:https://woniu336.github.io/ 魔改hugo主题: https://github.com/woniu336/hugo-magic 2. 服务器配置 建立 git 用户 adduser git安装 git sudo apt …...

【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task04 RAG模型 人话八股文Bakwaan_Buddy项目创空间部署
【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task04 RAG模型 人话八股文Bakwaan_Buddy项目创空间部署 什么是RAG: 我能把这个过程理解为Kimi.ai每次都能列出的一大堆网页参考资料吗?Kimi学了这些资料以后,根据这里面的信息综…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...

【JVM面试篇】高频八股汇总——类加载和类加载器
目录 1. 讲一下类加载过程? 2. Java创建对象的过程? 3. 对象的生命周期? 4. 类加载器有哪些? 5. 双亲委派模型的作用(好处)? 6. 讲一下类的加载和双亲委派原则? 7. 双亲委派模…...