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

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

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

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

Android——横屏竖屏
系统配置变更的处理机制 为了避免横竖屏切换时重新加载界面的情况,Android设计了一中配置变更机制,在指定的环境配置发生变更之时,无需重启活动页面,只需执行特定的变更行为。该机制的视线过程分为两步: 修改 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过程的驱动分析
往期内容 本专栏往期内容: Pinctrl子系统和其主要结构体引入Pinctrl子系统pinctrl_desc结构体进一步介绍Pinctrl子系统中client端设备树相关数据结构介绍和解析inctrl子系统中Pincontroller构造过程驱动分析:imx_pinctrl_soc_info结构体 input子系统专栏…...

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

00-开发环境 MPLAB IDE 配置
MPLAB IDE V8.83 File 菜单简介 New (CtrlN): 创建一个新文件,用于编写新的代码。 Add New File to Project...: 将新文件添加到当前项目中。 Open... (CtrlO): 打开现有文件。 Close (CtrlE): 关闭当前打开的文件。 …...

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

C++ 实现俄罗斯方块游戏
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

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

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

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

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

11.1组会汇报-基于区块链的安全多方计算研究现状与展望
基础知识 *1.背书,这个词源来自银行票据业务,是指票据转让时,原持有人在票据背面加盖自己的印鉴,证明该票据真实有效、如果有问题就可以找原持有人。 区块链中的背书就好理解了。可以简单的理解为验证交易并声明此交易合法&…...

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

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

如何下载安装TestLink?
一、下载TestLink、XAMPP TestLink 下载 |SourceForge.net 备用:GitHub - TestLinkOpenSourceTRMS/testlink-code: TestLink开源测试和需求管理系统 下载XAMPP: Download XAMPP 注意:TestLink与PHP版本有关系,所以XA…...

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

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

GESP4级考试语法知识(捕捉异常)
参考程序代码: #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> 是一种重要的标签,用于提供关于文档的信息,这些信息不直接显示在网页内容中,但对于…...

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

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

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

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

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

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

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