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

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 阿里云视频直播开发流程

首先来看一下直播效果 推流工具有很多种&#xff08;例如OBS、阿里云直播Demo推流、等等&#xff0c;我用的是芯象导播&#xff09;阿里播放器地址 一、直播基础服务概述 官方文档说明 二、直播域名配置需要两个域名&#xff08;推流域名、播流域名&#xff09; 官方文档说…...

SQLite 轻量级的嵌入式关系型数据库的替代软件

SQLite 是一个轻量级的嵌入式关系型数据库&#xff0c;由于其简单易用和跨平台的特性&#xff0c;被广泛应用于各种应用程序中。以下是一些可作为SQLite替代品的数据库软件或可视化管理工具&#xff1a; 1. **SQLiteStudio**&#xff1a;这是一个免费、开源的跨平台SQLite数据…...

Flutter-自适用高度PageView

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

群晖NAS本地搭建可远程交互的大型语言模型LLM聊天机器人

文章目录 前言1. 拉取相关的Docker镜像2. 运行Ollama 镜像3. 运行Chatbot Ollama镜像4. 本地访问5. 群晖安装Cpolar6. 配置公网地址7. 公网访问8. 固定公网地址 前言 本文主要分享如何在群晖NAS本地部署并运行一个基于大语言模型Llama 2的个人本地聊天机器人并结合内网穿透工具…...

TypeScript 构建工具之 webpack

在实际开发中&#xff0c;直接使用TypeScript 编译器的情况不多。 在项目中&#xff0c;需要使用构建工具对代码进行打包&#xff0c;不可能脱离项目使用TypeScript 编译器单独打包TypeScript 。 那如何将 webpack 和 TypeScript 进行集成&#xff1f; 参考文档&#xff1a; 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需求&#xff0c;这个时候我们就用contenteditable"true"来将别的标签修改为可编辑状态&#xff0c;但当我们通过js修改了内容之后光标的位置就是一个问题&…...

51单片机-LED灯蜂鸣器数码管按键DS18B20温度传感器

LDE灯的相关程序 LED灯闪烁 LED流水灯 方法1 方法二&#xff1a; 因为P1口可以直接控制P1^0~P1^7的8个led灯&#xff0c;利用一个8位的二进制数字来进行控制即可。如果要点亮P1^0 只需要给P1口传递 1111 1110即可。 蜂鸣器的使用 什么是蜂鸣器&#xff1f; 蜂鸣器是一种一…...

笔记本一线品牌有哪些

笔记本电脑的一线品牌通常指的是在市场上具有较高市场份额、良好口碑、较强的技术实力和服务能力的品牌。根据目前的信息&#xff0c;笔记本电脑市场的一线品牌主要包括以下几个&#xff1a; 联想 (Lenovo)&#xff1a;联想在全球笔记本市场上的占有率较高&#xff0c;其产品线…...

mysql聚合函数和分组

我最近开了几个专栏&#xff0c;诚信互三&#xff01; > |||《算法专栏》&#xff1a;&#xff1a;刷题教程来自网站《代码随想录》。||| > |||《C专栏》&#xff1a;&#xff1a;记录我学习C的经历&#xff0c;看完你一定会有收获。||| > |||《Linux专栏》&#xff1…...

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&#xff08;Web Application Firewall&#xff09;是一种安全系统&#xff0c;旨在监控和控制网络流量&#xff0c;以防止攻击&#xff0c;如SQL 注入、跨站脚本&#xff08;XSS&#xff09;和拒绝服务&#xff08;DoS&#xff09;。 WAF 可以通过多种方式绕过…...

HarmonyOS应用三之组件生命周期和参数传递

目录&#xff1a; 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&#xff0c;需要添加network模块 1.UDP Socket 1.核心API概览 主要的类有两个&#xff1a;QUdpSocket和QNetworkDatagramQUdpSocket表⽰⼀个UDP的socket⽂件 bind(const …...

读零信任网络:在不可信网络中构建安全系统21读后总结与感想兼导读

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

Java基础——注释

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

Redis未授权访问漏洞利用合集

一、基本信息 靶机&#xff1a;IP:192.168.100.40 攻击机&#xff1a;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#的在线考试管理系统

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

将 hugo 博客搬迁到服务器

1. 说明 在 Ubuntu 22.04 上使用 root 账号&#xff0c;创建普通账号&#xff0c;并赋予 root 权限。 演示站点&#xff1a;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&#xff1a; 我能把这个过程理解为Kimi.ai每次都能列出的一大堆网页参考资料吗&#xff1f;Kimi学了这些资料以后&#xff0c;根据这里面的信息综…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)

参考官方文档&#xff1a;https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java&#xff08;供 Kotlin 使用&#xff09; 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?

在大数据处理领域&#xff0c;Hive 作为 Hadoop 生态中重要的数据仓库工具&#xff0c;其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式&#xff0c;很多开发者常常陷入选择困境。本文将从底…...

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题

【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要&#xff1a; 近期&#xff0c;在使用较新版本的OpenSSH客户端连接老旧SSH服务器时&#xff0c;会遇到 "no matching key exchange method found"​, "n…...

MinIO Docker 部署:仅开放一个端口

MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

vue3 daterange正则踩坑

<el-form-item label"空置时间" prop"vacantTime"> <el-date-picker v-model"form.vacantTime" type"daterange" start-placeholder"开始日期" end-placeholder"结束日期" clearable :editable"fal…...

STM32标准库-ADC数模转换器

文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”&#xff1a;输入模块&#xff08;GPIO、温度、V_REFINT&#xff09;1.4.2 信号 “调度站”&#xff1a;多路开关1.4.3 信号 “加工厂”&#xff1a;ADC 转换器&#xff08;规则组 注入…...

【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析

1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器&#xff08;TI&#xff09;推出的一款 汽车级同步降压转换器&#xff08;DC-DC开关稳压器&#xff09;&#xff0c;属于高性能电源管理芯片。核心特性包括&#xff1a; 输入电压范围&#xff1a;2.95V–6V&#xff0c;输…...