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

环信鸿蒙IM SDK实现附件消息发送与下载

环信HarmonyOS IM SDK 正式版已经发布,该版本全面覆盖即时通讯(IM)的核心功能,为用户提供了完整的IM全功能体验,同时支持从Android APK到 NEXT 的数据迁移,更好地满足企业在不同业务场景下的适配需求。

点此下载最新版环信 HarmonyOS IM SDK

环信鸿蒙IM SDK 除文本消息外,还支持发送附件类型消息,包括语音、图片、视频和文件消息。本文为大家介绍从系统获取系统图片,视频,语音文件,使用sdk进行发送与下载。

一、发送附件消息
1、发送图片消息

① 权限添加
在对应模块的module.json5配置中,添加requestPermissions节点和对应权限的配置:

{"name": "ohos.permission.READ_MEDIA","reason": "$string:app_name","usedScene": {"abilities": ["EntryAbility"]}
},
{"name": "ohos.permission.WRITE_MEDIA","reason": "$string:app_name","usedScene": {"abilities": ["EntryAbility"]}
}

② 权限检测

//检查权限
export  async function checkAppPermission(context:common.UIAbilityContext): Promise<boolean> {try {const READ_MEDIA_PERMISSION: Permissions = 'ohos.permission.READ_MEDIA' //媒体读取权限const WRITE_MEDIA_PERMISSION: Permissions = 'ohos.permission.WRITE_MEDIA' //媒体写入权限let permissionList: Permissions[] = []; //需要申请选项列表let readPermission = await checkPermissions(READ_MEDIA_PERMISSION)//检查是否有媒体读取权限!readPermission && permissionList.push(READ_MEDIA_PERMISSION)let writePermission = await checkPermissions(WRITE_MEDIA_PERMISSION)//检查是否有媒体写入权限!writePermission && permissionList.push(READ_MEDIA_PERMISSION)if (permissionList.length) {//申请权限let res: boolean = await applyPermission(context, permissionList)if (!res) {//用户未同意授权AlertDialog.show({title: "提示",message: "无权限读写用户外部存储中的媒体文件信息,请前往系统设置开启",alignment: DialogAlignment.Center,secondaryButton: {value: '关闭',action: () => {}}})
}
return res
}
return true}catch (e) {return Promise.reject(e)
}
}

检查用户权限:

//检查用户权限
//@params permissions:权限名称数组
export  async function checkPermissions(permissions: Permissions): Promise<boolean> {try {let grantStatus: abilityAccessCtrl.GrantStatus = await checkAccessToken(permissions);return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED}catch (e) {return Promise.reject(e)}
}

从相册中选择图片:

 //从相册选择let PhotoSelectOptions = new picker.PhotoSelectOptions();PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;PhotoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();photoPicker.select(PhotoSelectOptions).then(async (PhotoSelectResult) => {if (PhotoSelectResult.photoUris.length) {//复制图片到缓存目录(缓存目录才有读写权限)let filePath = PhotoSelectResult.photoUris[0]if (filePath) {//这里获取到的filePath 就是图片的路径}}

发送方调用 createImageSendMessage 方法传入图片的本地资源标志符 URI、设置是否发送原图以及接收方的用户 ID (群聊或聊天室分别为群组 ID 或聊天室 ID)创建图片消息,然后调用 sendMessage 方法发送该消息。SDK 会将图片上传至环信服务器,服务器自动生成图片缩略图。

这里imageFilePathOrUri 为上面获取到的filePath
// `imageFilePathOrUri` 为图片本地路径或者Uri。
let message = ChatMessage.createImageSendMessage(toChatUsername, imageFilePathOrUri);
// 会话类型,包含 `Chat`、`GroupChat` 和 `ChatRoom`,表示单聊、群聊或聊天室,默认为单聊。
message.setChatType(ChatType.GroupChat);
// 发送消息
ChatClient.getInstance().chatManager()?.sendMessage(message);
2、发送视频消息

① 缩略图获取

import picker from '@ohos.file.picker';
import {checkAppPermission} from '../../utils/permissions/CheckAccess'
import { common } from '@kit.AbilityKit';
import camerapicker from '@ohos.multimedia.cameraPicker';
import camera from '@ohos.multimedia.camera';
import { BusinessError } from '@ohos.base';
import { ChatMessage, } from 'easemob'
import  SendMessages from '../../presenter/SendMessage'
//全局公共样式
@Styles
function fillScreen() {.width('100%').backgroundColor(Color.White).borderRadius(20).padding(10)
}@CustomDialog
@Component
export struct AttachmentMessageDialog {@Prop conversationId: ResourceStr@Prop imgMsg: ChatMessage|undefined = undefinedchangeMessage = (imgMsg: ChatMessage|undefined)=> {}private controller: CustomDialogControllerprivate context = getContext(this) as common.UIAbilityContext; //UIAbilityContextbuild() {Column() {Row(){Image($r('app.media.ease_chat_takepic_pressed')).width(20).height(20)Text("相机").fontColor('#33b1ff')}.width('100%').padding({top:30,left:20,bottom:20}).onClick(async ()=>{this.camera()})Row(){}.width('100%').backgroundColor("#e7e9eb").height(1)Row(){Image($r('app.media.ease_chat_image_pressed')).width(20).height(20)Text("相册").fontColor('#33b1ff')}.width('100%').padding({top:20,left:20,bottom:20}).onClick(async () =>{this.photo()})Row(){}.width('100%').backgroundColor("#e7e9eb").height(1)Row(){Image($r('app.media.em_chat_video_pressed')).width(20).height(20)Text("视频").fontColor('#33b1ff')}.width('100%').padding({top:20,left:20,bottom:20}).onClick(async ()=>{ this.video()})Row(){}.width('100%').backgroundColor("#e7e9eb").height(1)Row(){Image($r('app.media.em_chat_card_normal')).width(20).height(20)Text("名片").fontColor('#33b1ff')}.width('100%').padding({top:20,left:20,bottom:20}).onClick( ()=>{})Row(){}.width('100%').backgroundColor("#e7e9eb").height(7)Row(){Text("取消").fontColor('#33b1ff')}.padding({top:20,left:20,bottom:20})}.width('100%')}private async photo(){//检查是否有读写外部媒体权限let res: boolean = await checkAppPermission(this.context)//无权限返回if (!res) return//从相册选择let PhotoSelectOptions = new picker.PhotoSelectOptions();PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;PhotoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();photoPicker.select(PhotoSelectOptions).then(async (PhotoSelectResult) => {if (PhotoSelectResult.photoUris.length) {//复制图片到缓存目录(缓存目录才有读写权限)let filePath = PhotoSelectResult.photoUris[0]if (filePath) {SendMessages.sendImageMessage(this.conversationId+"", filePath).then((value)=>{this.changeMessage(value)})}}})}private async camera(){//检查是否有读写外部媒体权限let res: boolean = await checkAppPermission(this.context)//无权限返回if (!res) returntry {let pickerProfile: camerapicker.PickerProfile = {cameraPosition: camera.CameraPosition.CAMERA_POSITION_BACK};let pickerResult: camerapicker.PickerResult = await camerapicker.pick(this.context,[camerapicker.PickerMediaType.PHOTO, camerapicker.PickerMediaType.PHOTO], pickerProfile);if(pickerResult?.resultUri){}} catch (error) {let err = error as BusinessError;console.error(`the pick call failed. error code: ${err.code}`);}}private async video(){console.log(this.conversationId+"------")//检查是否有读写外部媒体权限let res: boolean = await checkAppPermission(this.context)//无权限返回if (!res) return//从相册选择let PhotoSelectOptions = new picker.PhotoSelectOptions();PhotoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.VIDEO_TYPE;PhotoSelectOptions.maxSelectNumber = 1;let photoPicker = new picker.PhotoViewPicker();photoPicker.select(PhotoSelectOptions).then(async (PhotoSelectResult) => {if (PhotoSelectResult.photoUris.length) {//复制图片到缓存目录(缓存目录才有读写权限)let filePath = PhotoSelectResult.photoUris[0]if (filePath) {SendMessages.sendVideoMessage(this.conversationId+"", filePath,this.context).then((value)=>{this.changeMessage(value)})console.log("PhotoSelectOptions",filePath)}}})}
}

②发送方调用 createVideoSendMessage 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)、视频文件的本地路径、视频时长以及缩略图的本地存储路径,然后调用 sendMessage 方法发送消息。SDK 会将视频文件上传至消息服务器。若需要视频缩略图,你需自行获取视频首帧的路径,将该路径传入 createVideoSendMessage 方法。
上面获取到视频首诊

// 在应用层获取视频首帧
let thumbPath = this.getThumbPath(videoPath);
let message = ChatMessage.createVideoSendMessage(toChatUsername, videoPath, videoLength, thumbPath);
if (!message) {return;
}
// 会话类型,包含 `Chat`、`GroupChat` 和 `ChatRoom`,表示单聊、群聊或聊天室,默认为单聊。
message.setChatType(ChatType.GroupChat);
// 发送消息
ChatClient.getInstance().chatManager()?.sendMessage(message);
3、发送语音消息

①音频录制

// 音频录制
import { media } from '@kit.MediaKit';
import { BusinessError, systemDateTime } from '@kit.BasicServicesKit';
import fs from '@ohos.file.fs';
class AudioRecorder {private avRecorder: media.AVRecorder | undefined = undefined;public maxAmplitude: number = 0;public audioPath = ""private avProfile: media.AVRecorderProfile = {audioBitrate: 100000, // 音频比特率audioChannels: 2, // 音频声道数audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式,当前只支持aacaudioSampleRate: 48000, // 音频采样率fileFormat: media.ContainerFormatType.CFT_MPEG_4A, // 封装格式,当前只支持m4a};private avConfig: media.AVRecorderConfig = {audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC, // 音频输入源,这里设置为麦克风profile: this.avProfile,url: 'fd://35', // 参考应用文件访问与管理开发示例新建并读写一个文件};// 注册audioRecorder回调函数setAudioRecorderCallback(): void {if (this.avRecorder !== undefined) {// 状态机变化回调函数this.avRecorder.on('stateChange', (state: media.AVRecorderState, _: media.StateChangeReason) => {console.log(`AudioRecorder current state is ${state}`);})// 错误上报回调函数this.avRecorder.on('error', (err: BusinessError) => {console.error(`AudioRecorder failed, code is ${err.code}, message is ${err.message}`);})}}// 开始录制对应的流程async startRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined) {await this.avRecorder.release();this.avRecorder = undefined;}// 1.创建录制实例this.avRecorder = await media.createAVRecorder();this.setAudioRecorderCallback();// 2.获取录制文件fd赋予avConfig里的url;参考FilePicker文档const context = getContext(this);const path = context.filesDir;let currentTimeMillis: number = new Date().getTime();const filepath = path + '/' + currentTimeMillis + '.wav';this.audioPath = filepathconst file = fs.openSync(filepath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);const fdNumber = file.fd;this.avConfig.url = 'fd://' + fdNumber;// 3.配置录制参数完成准备工作await this.avRecorder.prepare(this.avConfig);// 4.开始录制await this.avRecorder.start();}// 暂停录制对应的流程async pauseRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined && this.avRecorder.state === 'started') { // 仅在started状态下调用pause为合理状态切换await this.avRecorder.pause();}}// 恢复录制对应的流程async resumeRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined && this.avRecorder.state === 'paused') { // 仅在paused状态下调用resume为合理状态切换await this.avRecorder.resume();}}// 停止录制对应的流程async stopRecordingProcess(): Promise<void> {if (this.avRecorder !== undefined) {// 1. 停止录制if (this.avRecorder.state === 'started'|| this.avRecorder.state === 'paused') { // 仅在started或者paused状态下调用stop为合理状态切换await this.avRecorder.stop();}// 2.重置await this.avRecorder.reset();// 3.释放录制实例await this.avRecorder.release();this.avRecorder = undefined;}}
}let audioRecorder = new AudioRecorder()
export default audioRecorder as AudioRecorder

②发送方调用 createVoiceSendMessage 方法传入接收方的用户 ID(群聊或聊天室分别为群组 ID 或聊天室 ID)、语音文件的路径和语音时长创建语音消息,然后调用 sendMessage 方法发送消息。SDK 会将语音文件上传至环信服务器

// `filePathOrUri` 为语音文件的本地路径或者文件的 URI,`duration` 为语音时长(单位为秒)。
let message = ChatMessage.createVoiceSendMessage(to, filePathOrUri, duration);
if (!message) {return;
}
// 设置会话类型,即`ChatMessage` 类的 `ChatType` 属性,包含 `Chat`、`GroupChat` 和 `ChatRoom`,表示单聊、群聊或聊天室,默认为单聊。
message.setChatType(ChatType.GroupChat);
// 发送消息
ChatClient.getInstance().chatManager()?.sendMessage(message);
二、接收附件消息

1、接收语音文件
接收方收到 onMessageReceived 回调,调用 getRemoteUrl 或 getLocalPath 方法获取语音文件的服务器地址或本地路径,从而获取语音文件。

let voiceBody = message.getBody() as VoiceMessageBody;
// 获取语音文件在服务器的地址。
let voiceRemoteUrl = voiceBody.getRemoteUrl();
// 本地语音文件的本地路径。
let voiceLocalPath = voiceBody.getLocalPath();

2、接收图片消息
接收方收到图片消息,自动下载图片缩略图。
SDK 默认自动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 true。若设置为手动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 false ,需调用 ChatClient.getInstance().chatManager()?.downloadThumbnail(message) 下载。
接收方收到 onMessageReceived 回调,调用 downloadAttachment 下载原图。

let msgListener: ChatMessageListener = {onMessageReceived: (messages: ChatMessage[]): void => {messages.forEach( message => {if (message.getType() === ContentType.IMAGE) {let callback: ChatCallback = {onSuccess: (): void => {// 附件下载成功},onError: (code: number, error: string): void => {// 附件下载失败},onProgress: (progress: number): void => {// 附件下载进度}}message.setMessageStatusCallback(callback);// 下载附件ChatClient.getInstance().chatManager()?.downloadAttachment(message);}})}
}

3、接收视频消息
接收方收到视频消息时,自动下载视频缩略图。
SDK 默认自动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 true 。若设置为手动下载缩略图,即 ChatOptions.setAutoDownloadThumbnail 设置为 false,需调用 ChatClient.getInstance().chatManager()?.downloadThumbnail(message) 下载。
接收方收到 onMessageReceived 回调,可以调用 ChatClient.getInstance().chatManager()?.downloadAttachment(message) 方法下载视频原文件

let msgListener: ChatMessageListener = {onMessageReceived: (messages: ChatMessage[]): void => {messages.forEach( message => {if (message.getType() === ContentType.VIDEO) {let callback: ChatCallback = {onSuccess: (): void => {// 附件下载成功},onError: (code: number, error: string): void => {// 附件下载失败},onProgress: (progress: number): void => {// 附件下载进度}}message.setMessageStatusCallback(callback);// 下载附件ChatClient.getInstance().chatManager()?.downloadAttachment(message);}})}
}

获取视频缩略图和视频原文件。

let body = message.getBody() as VideoMessageBody;
// 从服务器端获取视频文件。
let imgRemoteUrl = body.getRemoteUrl();
// 从服务器获取视频缩略图文件。
let thumbnailUrl = body.getThumbnailRemoteUrl();
// 从本地获取视频文件文件。
let localPath = body.getLocalPath();
// 从本地获取视频缩略图文件。
let localThumbPath = body.getThumbnailLocalPath();

参考文档:

  • 注册环信IM:https://console.easemob.com/user/register

  • 环信官方Demo下载:https://www.easemob.com/download/demo

相关文章:

环信鸿蒙IM SDK实现附件消息发送与下载

环信HarmonyOS IM SDK 正式版已经发布&#xff0c;该版本全面覆盖即时通讯&#xff08;IM&#xff09;的核心功能&#xff0c;为用户提供了完整的IM全功能体验&#xff0c;同时支持从Android APK到 NEXT 的数据迁移&#xff0c;更好地满足企业在不同业务场景下的适配需求。 点…...

探索NetCat:网络流量监测与数据传输的利器

从简单的数据传输到复杂的网络调试&#xff0c;NetCat的灵活性和多功能性让人赞叹不已&#xff0c;在这篇文章中我将深入探讨NetCat的魅力&#xff0c;揭示它的基本功能、实用技巧以及在日常工作中的应用场景&#xff0c;发现如何用这一小工具提升的网络技能与效率。 目录 Net…...

【运动的&足球】足球运动员球守门员裁判检测系统源码&数据集全套:改进yolo11-DBBNCSPELAN

改进yolo11-FocalModulation等200全套创新点大全&#xff1a;足球运动员球守门员裁判检测系统源码&#xff06;数据集全套 1.图片效果展示 项目来源 人工智能促进会 2024.10.28 注意&#xff1a;由于项目一直在更新迭代&#xff0c;上面“1.图片效果展示”和“2.视频效果展示…...

求最大公约数,最小公倍数

输入两个正整数 m 和 n&#xff0c;求其最大公约数和最小公倍数。 求最小公倍数算法&#xff1a; 最小公倍数 两整数的乘积 最大公约数 根据求最小公倍数的算法&#xff0c;可以看出如果已知最大公约数&#xff0c;就能很容易求出最小公倍数。而通过辗转相除法和相减法&#…...

Android——横屏竖屏

系统配置变更的处理机制 为了避免横竖屏切换时重新加载界面的情况&#xff0c;Android设计了一中配置变更机制&#xff0c;在指定的环境配置发生变更之时&#xff0c;无需重启活动页面&#xff0c;只需执行特定的变更行为。该机制的视线过程分为两步&#xff1a; 修改 Androi…...

scala---10.30

val、var package com_1030class Person {var name:String"rose"def sum(n1:Int,n2:Int):Int{n1n2} } object Person{def main(args: Array[String]): Unit {//创建person对象var personnew Person()println(person.sum(10,20))//30println(person.name)person.nam…...

Pinctrl子需要中client端使用pinctrl过程的驱动分析

往期内容 本专栏往期内容&#xff1a; Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析&#xff1a;imx_pinctrl_soc_info结构体 input子系统专栏…...

【网络】传输层协议TCP

目录 四位首部长度 序号 捎带应答 标记位 超时重传机制 连接管理机制&#xff08;RST标记位&#xff09; 三次握手及四次挥手的原因 TCP的全称是传输控制协议&#xff08;Transmission Control Protocol&#xff09;&#xff0c;也就是说&#xff0c;对于放到TCP发送缓冲…...

00-开发环境 MPLAB IDE 配置

MPLAB IDE V8.83 File 菜单简介 New (CtrlN)&#xff1a; 创建一个新文件&#xff0c;用于编写新的代码。 Add New File to Project...&#xff1a; 将新文件添加到当前项目中。 Open... (CtrlO)&#xff1a; 打开现有文件。 Close (CtrlE)&#xff1a; 关闭当前打开的文件。 …...

<meta property=“og:type“ content=“website“>

<meta property"og:type" content"website"> ​ 这段代码是HTML中的一部分&#xff0c;具体来说&#xff0c;它是一个用于定义Open Graph协议的meta标签。 代码分析 <meta> 标签&#xff1a;这是一个HTML标签&#xff0c;用于在HTML文档的头…...

C++ 实现俄罗斯方块游戏

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

QT打包Macosx应用发布App Store简易流程

1、QC里编译工程&#xff0c;生成Release版的的app文件&#xff1b; 2、运行macdeployqt把需要的文件打包进app文件中&#xff1b; % ~/Qt/5.15.0/clang_64/bin/macdeployqt {编译的app文件所在路径}/Release/xxxx.app 3、使用codesign对app进行签名&#xff0c;如果要发App…...

untiy mlagents 飞机大战 ai训练

前言 之前那个python源码的飞机大战bug过多&#xff0c;还卡顿&#xff0c;难以继续训练。可直接放弃的话又不甘心&#xff0c;所以找了个unity版本的飞机大战继续(终于不卡了)&#xff0c;这次直接使用现成的mlagents库。 过程 前前后后花了两周时间&#xff0c;甚至因此拖…...

从0开始学统计-什么是中心极限定理

引言 中心极限定理&#xff08;Central Limit Theorem, CLT&#xff09;是统计学中的一块基石&#xff0c;它揭示了一个难以置信的数学现象&#xff1a;无论一个随机变量的原始分布如何&#xff0c;只要我们取足够大的样本量&#xff0c;这些样本的平均值&#xff08;或总和&a…...

工具方法 - 个人活动的分类

人类活动的分类是一个复杂的话题&#xff0c;因为人类的活动范围非常广泛且相互交叉。然而&#xff0c;我们可以尝试将人类的活动大致分为以下几个主要类别&#xff1a; 工作活动 工作活动是人类生活中不可或缺的一部分&#xff0c;通常包括以下方面&#xff1a; 1. 职业工作&a…...

11.1组会汇报-基于区块链的安全多方计算研究现状与展望

基础知识 *1.背书&#xff0c;这个词源来自银行票据业务&#xff0c;是指票据转让时&#xff0c;原持有人在票据背面加盖自己的印鉴&#xff0c;证明该票据真实有效、如果有问题就可以找原持有人。 区块链中的背书就好理解了。可以简单的理解为验证交易并声明此交易合法&…...

ubuntu【桌面】 配置NAT模式固定IP

DHCP分配导致虚拟机IP老变&#xff0c;SSH老要重新配置&#xff0c;设成静态方便些 一、设NAT模式 1、设为NAT模式 2、看模式对应的虚拟网卡 - VMnet8 3、共享主机网卡网络到虚拟网卡 - VMnet8 二、为虚拟网卡设置静态IP 记住这个IP 三、设置ubuntu固定IP 1、关闭DHCP并…...

评估 机器学习 回归模型 的性能和准确度

回归 是一种常用的预测模型&#xff0c;用于预测一个连续因变量和一个或多个自变量之间的关系。 那么&#xff0c;最后评估 回归模型 的性能和准确度非常重要&#xff0c;可以帮助我们判断模型是否有效并进行改进。 接下来&#xff0c;和大家分享如何评估 回归模型 的性能和准…...

如何下载安装TestLink?

一、下载TestLink、XAMPP TestLink 下载 |SourceForge.net 备用&#xff1a;GitHub - TestLinkOpenSourceTRMS/testlink-code&#xff1a; TestLink开源测试和需求管理系统 下载XAMPP&#xff1a; Download XAMPP 注意&#xff1a;TestLink与PHP版本有关系&#xff0c;所以XA…...

基于SSM+微信小程序的订餐管理系统(点餐2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的订餐管理系统实现了管理员和用户。管理端实现了 首页、个人中心、用户管理、菜品分类管理、菜品信息管理、订单信息管理、配送信息管理、菜品评价管理、订单投诉管理、…...

【C++排序 双指针】1996. 游戏中弱角色的数量|1996

本文涉及的基础知识点 排序 C算法&#xff1a;滑动窗口及双指针总结 本题其它解法 【C单调栈 排序】1996. 游戏中弱角色的数量|1996 LeetCode1996. 游戏中弱角色的数量 你正在参加一个多角色游戏&#xff0c;每个角色都有两个主要属性&#xff1a;攻击 和 防御 。给你一个…...

GESP4级考试语法知识(捕捉异常)

参考程序代码&#xff1a; #include <iostream> using namespace std;double divide(double a, double b) {if (b 0) {throw "Division by zero error"; // 抛出异常}return a / b; }int main() {double num1, num2;cout << "Enter two numbers:…...

HTML 基础标签——元数据标签 <meta>

文章目录 1. `<meta>` 标签概述2. 属性详解2.1 `charset` 属性2.2 `name` 属性2.3 `content` 属性2.4 `http-equiv` 属性3. 其他常见属性小结在 HTML 文档中,元数据标签 <meta> 是一种重要的标签,用于提供关于文档的信息,这些信息不直接显示在网页内容中,但对于…...

栈虚拟机和寄存器虚拟机,有什么不同?

本来这节内容是打算直接讲字节码指令的&#xff0c;但讲之前又必须得先讲指令集架构&#xff0c;而指令集架构又分为两种&#xff0c;一种是基于栈的&#xff0c;一种是基于寄存器的。 那不妨我们这节就单独来讲讲栈虚拟机和寄存器虚拟机&#xff0c;它们有什么不同&#xff0…...

Windows下基于fping进行批量IP测试

fping是Linux下一个很好用的IP测试工具&#xff0c;结合代码可以完成批量的IP测试&#xff0c;在网络调试中用途很广。本文是基于fping for Windows结合bat批处理&#xff0c;定制的测试脚本样例。 一、程序信息 本次测试使用fpingV5.1 for Windows版&#xff0c;版本信息如下…...

一款实用的Word文档图片转换与水印保护工具

目录 前言软件功能简介软件实现方法及关键代码 1. Word 文档转图片的实现2. 图片水印添加功能3. 生成数字指纹&#xff08;哈希值&#xff09;4. 保存图片信息到 JSON 文件 软件的实际使用场景软件操作指南 1. 下载和安装2. 操作流程 总结 1&#xff0c;前言 在日常办公和内…...

优化用于传感应用的衬底集成波导技术

ANSYS HFSS 是一款功能强大的电磁仿真软件&#xff0c;支持为微流体生物传感器应用设计和分析衬底集成波导 &#xff08;SIW&#xff09; 技术。它为快速设计优化、材料选择、系统集成和虚拟原型制作提供了一个强大的平台。借助 ANSYS HFSS&#xff0c;研究人员和工程师可以高效…...

Java多态特性的向上转型

Java的多态特性通过向上转型来实现。向上转型指的是将子类对象赋值给父类引用变量的操作。这样做的好处是可以使用父类引用变量来调用子类对象的方法。 例如&#xff0c;有一个父类Animal和一个子类Dog&#xff0c;可以这样进行向上转型&#xff1a; Animal animal new Dog(…...

C++ 判断语句的深入解析

C 判断语句的深入解析 C 是一种广泛使用的编程语言&#xff0c;以其高效性和灵活性著称。在 C 中&#xff0c;判断语句是控制程序流程的关键组成部分&#xff0c;它们允许程序根据不同的条件执行不同的代码路径。本文将深入探讨 C 中的判断语句&#xff0c;包括 if、else if、…...

15分钟学 Go 第 33 天:项目结构

第33天&#xff1a;项目结构 目标&#xff1a;了解Go项目的典型结构 在Go语言的开发中&#xff0c;项目结构的合理性直接影响着代码的可维护性、可扩展性和团队协作效率。本篇文章将深入探讨Go语言的典型项目结构&#xff0c;并提供实际示例代码和相关的流程图。 一、Go项目…...