当前位置: 首页 > 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;根据这里面的信息综…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; 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集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; 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&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

稳定币的深度剖析与展望

一、引言 在当今数字化浪潮席卷全球的时代&#xff0c;加密货币作为一种新兴的金融现象&#xff0c;正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而&#xff0c;加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下&#xff0c;稳定…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

【JVM面试篇】高频八股汇总——类加载和类加载器

目录 1. 讲一下类加载过程&#xff1f; 2. Java创建对象的过程&#xff1f; 3. 对象的生命周期&#xff1f; 4. 类加载器有哪些&#xff1f; 5. 双亲委派模型的作用&#xff08;好处&#xff09;&#xff1f; 6. 讲一下类的加载和双亲委派原则&#xff1f; 7. 双亲委派模…...