news 2026/10/22 0:47:40

gifdrawable4cj:基于仓颉(Cangjie)的 GIF 图像渲染库接入与解码原理实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
gifdrawable4cj:基于仓颉(Cangjie)的 GIF 图像渲染库接入与解码原理实战
  • OpenHarmony
  • UI组件
  • 图像处理

【免费下载链接】gifdrawable4cj

一个gif图像加载库

项目地址:https://gitcode.com/Cangjie-TPC/gifdrawable4cj
点击查看免费下载

gifdrawable4cj是一个使用仓颉(Cangjie)语言实现的 GIF 图像渲染库,面向 OpenHarmony/HarmonyOS 应用(HAP)与模块(HAR)场景,提供从 GIF 二进制数据解析、帧解码到画布播放的完整链路。本文以仓库根目录的 README.md 为主线,结合 doc/feature_api.md 中的完整 API 说明与gifdrawable模块源码,带你掌握:如何在工程中引入该库、用GIFComponent组件播放 GIF、通过ControllerOptions精细控制播放行为,以及底层基于 Schema 的流式解析与 LZW 解压是如何工作的。

项目定位与核心特性

gifdrawable4cj 由entry(示例应用 HAP 模块)与gifdrawable(可复用 HAR 库模块)两个工程模块组成,核心代码全部使用仓颉语言编写。作为图像加载库,它解决的问题是:在仓颉 UI 应用中,把工程资源、沙箱文件或网络下载得到的 GIF 数据解码为逐帧图像,并交给自定义组件驱动播放。

仓库 README.md 明确列出以下特性:

  • 支持播放 GIF 图片:提供开箱即用的自定义组件GIFComponent;
  • 支持控制 GIF 播放/暂停:通过autoPlay状态变量一键切换;
  • 支持调节 GIF 播放速率:ControllerOptions.setSpeedFactor()支持加速与减速;
  • 支持设置显示大小:组件宽高由canvasWidth/canvasHeight状态驱动;
  • 支持不同的拉伸类型:内置ScaleType枚举,覆盖 7 种缩放与对齐策略。

软件架构:模块划分与数据流

架构图

README 提供了两张与解码主题强相关的示意图。第一张展示 GIF 图片内部的主要结构(逻辑屏幕描述符、颜色表、图像描述符、扩展块等),第二张给出从原始字节到可播放帧的解析流程图:

源码目录结构

README 给出的仓库目录骨架如下,其中gifdrawable/src/main/cangjie/src下四个子目录各司其职:

├─AppScope ├─doc ├─entry │ └─src │ └─main │ ├─cangjie │ └─resources ├─gifdrawable │ └─src │ └─main │ ├─cangjie │ │ └─src │ │ ├─gifdisplay │ │ ├─parser │ │ ├─sparkmd5 │ │ └─utils │ └─resources └─hvigor

各目录职责(与 README 目录说明一致):

  • AppScope:全局资源存放目录和应用全局信息配置目录;
  • doc:API 文档和使用手册存放目录,接口细节见 doc/feature_api.md;
  • entry:工程模块,编译生成一个 HAP,其中entry/src/main/cangjie是仓颉代码目录,resources是资源文件目录;
  • gifdrawable:工程模块,编译生成一个 HAR 包,即本文使用的库本体;
  • gifdrawable/src/main/cangjie/src/gifdisplay:组件主体代码目录,包含GIFComponent、ControllerOptions、ResourceLoader、FileUtils;
  • gifdrawable/src/main/cangjie/src/parser:帧解码目录,包含数据流解析(GIFUnit8)、Schema 解析(parseGIF)、LZW 解压、GIFFrame帧结构;
  • gifdrawable/src/main/cangjie/src/sparkmd5:文件哈希计算代码目录(用于网络下载文件的本地命名缓存);
  • gifdrawable/src/main/cangjie/src/utils:解码工具目录(URI 编解码、位运算等辅助工具);
  • hvigor:构建工具目录。

在gifdisplay目录下,gif_component.cj 是GIFComponent组件实现,controller_options.cj 是配置与解码入口,resource_loader.cj 负责三种数据来源的读取。

编译构建:以 module 方式接入工程

README 的"编译构建"章节给出完整的接入流程,需要在被集成的应用工程中完成以下 5 步:

第 1 步:克隆下载项目,将本仓库拉取到本地。

第 2 步:把gifdrawable模块整体拷贝到你的应用项目目录下(与entry同级)。

第 3 步:修改自身应用entry下的oh-package.json5,在dependencies字段添加对gifdrawable的本地文件依赖:

{ "name": "entry", "version": "1.0.0", "description": "Please describe the basic information.", "main": "", "author": "", "license": "", "dependencies": { "gifdrawable": "file:../gifdrawable" } }

第 4 步:修改自身应用entry/src/main/cangjie下的cjpm.toml,在[dependencies]字段下按相对路径声明库的仓颉源码依赖。仓库中 entry/src/main/cangjie/cjpm.toml 的写法即为参考:

[dependencies] [dependencies.gifdrawable] path = "../../../../gifdrawable/src/main/cangjie"

说明:该相对路径从entry/src/main/cangjie出发,向上回退四层到仓库根,再进入gifdrawable/src/main/cangjie,与 README 中的示例一致。实际接入时请以你自己的工程目录层级为准调整。

第 5 步:在项目中引用库的公开 API:

import gifdrawable.gifdisplay.* import gifdrawable.parser.*

其中gifdrawable.gifdisplay提供组件与控制器(GIFComponent、ControllerOptions、ResourceLoader),gifdrawable.parser提供解码与帧结构(parseBufferToFrame、GIFFrame、ScaleType等)。

功能示例:从数据加载到播放控制

README 的"功能示例"章节以"GIF 动图数据解析"为主线,给出完整可复制的调用序列,下面按步骤展开。

第 1 步:在 UI 中添加 GIFComponent 组件

在页面状态中声明控制器与各类控制变量,然后在build()中挂载组件:

// 创建一个控制器保证GIFComponent初始化 @State var model: ControllerOptions = ControllerOptions() // 是否自动播放 @State var gifAutoPlay: Bool = true // 重置GIF播放,每次取反都能生效 @State var gifReset: Bool = false // 绘制区域宽度 @State var compWidth: Float64 = 300.0 // 绘制区域高度 @State var compHeight: Float64 = 300.0 // 添加GIFComponent组件 GIFComponent( model: model, // 控制器 autoPlay: gifAutoPlay, // 自动播放标志 resetGif: gifReset, // 重置标志 canvasWidth: compWidth, // 绘制区域宽度 canvasHeight: compHeight // 绘制区域高度 )

从组件源码看,gif_component.cj 中model、autoPlay、resetGif都是@Watch+@Link双向绑定的状态变量:autoPlay变化触发watchAutoPlay(),为true时立即调用renderFrames()开始播放;resetGif取反会先取消定时器、清空画布、将帧索引归零后重新渲染;model变化则触发watchModel()等待画布尺寸就绪后重新解析。组件内部使用Canvas+CanvasRenderingContext2D绘制,并开启抗锯齿(RenderingContextSettings(antialias: true))。

第 2 步:初始化控制器

创建独立控制器实例进行数据预处理,处理完成后一次性赋值给组件绑定的model,可以减少组件刷新次数:

// 创建控制器 let modelx: ControllerOptions = ControllerOptions() // 设置是否使用硬解码 modelx.setOpenHardware(true) // 设置拉伸方式 modelx.setScaleType(ScaleType.FIT_CENTER) // 设置速率 modelx.setSpeedFactor(1) // 设置完成一次播放后的回调函数 modelx.setLoopFinish({loopTime: Option<Int64> => })

第 3 步:加载数据(三种方式)

ResourceLoader提供三种数据来源的读取接口(对应 resource_loader.cj 的实现):

// 读取工程内资源(resId 为媒体资源 ID,需要传入全局 AbilityContext) let buffer: Array<UInt8> = ResourceLoader.loadMedia(UInt32(@r(app.media.xxx).id), globalAbilityContext) // 读取沙箱路径下资源(仅支持 .gif 后缀文件) let buffer: Array<UInt8> = ResourceLoader.loadRawFile("/data/storage/el2/base/files/xxx.gif") // 下载网络资源(成功回调拿到 Array<Byte>,失败回调拿到错误信息) ResourceLoader.downloadDataWithContext( Model("网络资源地址"), // 下载设置 globalAbilityContext, // AbilityContext {buffer: Array<Byte> => // 下载成功并读取成 Array<Byte> 后的回调函数 }, { str: String => // 下载错误回调函数 } )

几个实现细节值得注意:

  • loadMedia(resId, context)通过ResourceManager.getResourceManager(stageContext).getMediaContent(resId, 0)读取媒体资源,context为Option<AbilityContext>,为None时返回空数组并输出错误日志;
  • loadRawFile(filename)在 resource_loader.cj 中会先校验文件名必须以.gif结尾,否则抛出Exception;
  • 网络下载走 OpenHarmony 下载代理(ohos.request.agent):下载前用SparkMD5.hashBinary(model.url, false)生成 URL 的 MD5 作为文件名,缓存到/data/storage/el2/base/files目录;Model(url, progressCallBack)可附带进度回调(进度区间 0%~100%),下载完成事件触发后自动读取文件内容并删除临时文件。

第 4 步:数据转换成可显示内容

将字节数据交给控制器的loadBuffer异步处理,回调中携带Option<String>错误信息:

// 调用数据处理方法 modelx.loadBuffer( buffer, // 步骤3中获取的数据 {error: Option<String> => if (let Some(v) <- error) { // 错误信息处理 } else { // 处理完成,开始显示 gifAutoPlay = true // 将已完成数据处理的控制器赋值给组件关联的初始化控制器 model = modelx } } )

loadBuffer在 controller_options.cj 中会根据openHardware分流:软解码走userTaskPoolParse(任务池解析 +createPixelMapAll批量生成 PixelMap),硬解码走useHardwareParse(系统 ImageSource 的createPixelMapList一次取出全部帧)。

显示控制

数据加载完成、开始播放之后,以下状态与接口即构成完整的播放控制面板:

// 开始 gifAutoPlay = true // 暂停 gifAutoPlay = false // 重置(每次取反都能生效) gifReset = !gifReset // 修改宽高 compWidth = 100.0 compHeight = 200.0 // 修改速率 model.setSpeedFactor(2) // 修改拉伸方式 model.setScaleType(ScaleType.FIT_START)

解码原理:从字节流到 GIFFrame 的完整链路

README 将详细接口指向 doc/feature_api.md,该文档按"GIF 解码器 → 二进制数据解析器 → 提取 gif 帧数据 → 动图播放 → 其他接口"的层次组织。结合parser目录源码,可以还原一条完整的解码调用链:

parseBufferToFrame(arraybuffer, patchRule) └─ parseGIF(byteData) // 包装 UInt8TypedArray,构造 GIFSchema └─ parse(stream, schema, result, par) // 循环/条件解析各 GIF 块 └─ decompressFrames(gif, false) // 遍历 Schema 帧列表 └─ decompressFrame(gce, image, gct) // 提取单帧像素 └─ lzw(minCodeSize, data, pixelCount) // LZW 解压 └─ deinterlace(pixels, width) // 隔行扫描重排 └─ prePatchGIF(origins, patchFunc) // 颜色表 → 可绘制 patch 流

数据流封装与逐字节读取

gif_uint8.cj 定义了UInt8TypedArray(字节数组 + 游标位置封装)与GIFUnit8(GIF 数据流解析类)。GIFUnit8提供一组高阶函数式的读取原语,全部以UInt8TypedArray为入参、移动内部游标:

  • readByte()/peekByte(offset!: Int64 = 0):读取/查看下一个字节;
  • readBytes(length)/peekBytes(length):读取/查看指定长度字节数组;
  • readString(length):读取指定长度并转成字符串(用于解析 GIF 头、注释等 ASCII 字段);
  • readUnsigned(littleEndian: Bool):将两个字节合成一个 16 位无符号整数,littleEndian决定合成顺序——这是 GIF 文件读取中固定的低位优先(little-endian)读取方式;
  • readArray(byteSize, totalFunc):分组读取数据,totalFunc负责计算读取次数,可结合当前/上次解析的Schema对象动态决定;
  • readBits(params: Array<Int64>):按(起始位置, 切割长度)的成对参数(如[0, 3, 3, 5])把一个字节的 8 个 bit 重新组合;
  • subBitsTotal(bits, startIndex, length):把指定 bit 段计算为十进制值。

Schema 驱动的流式解析框架

GIF 文件本质是若干块(Block)的串联:头、逻辑屏幕描述符、全局颜色表、图形控制扩展(GCE)、图像描述符、图像数据、尾块等。feature_api.md 用Schema体系抽象这一结构,gif_schemas.cj 中Schema是基类,GIFSchema(整体帧集合 + 全局颜色表gct)、ImageSchema(图像描述符,含局部颜色表lct与data_blocks)是其子类,各自重写parse(mStream, result, parent)与getTypeCode()。

配套的四个顶层解析函数构成组合子框架:

  • parse(stream, loops, result, par)/parse(stream, schema, result, par):按子类数据处理流程解析流中数据;
  • conditional(schema, conditionFunc):条件判断——满足条件才按当前 Schema 逻辑处理;
  • loop(schema, continueFunc):循环处理,continueFunc返回false时终止,防止无限循环。

parseGIF(byteData)(见 gif_parse.cj)就是这一框架的入口:创建UInt8TypedArray(byteData, 0)与GIFSchema,随后调用parse(stream, schema, schema, schema)完成整份数据的结构化解码。

LZW 解压缩与隔行扫描重排

GIF 图像数据采用 LZW 压缩编码。lzw(minCodeSize, data, pixelCount)(实现于 lzw.cj)按 GIF 规范维护prefix、suffix、pixelStack三张表(栈上限 4096),处理Clear Code、End of Information码,动态扩展code_size,最终输出长度恰为pixelCount的像素索引数组。

对于隔行扫描(Interlaced)存储的 GIF,解码后还需deinterlace(pixels, width)(gif_parse.cj)按offsets = [0, 4, 2, 1]、steps = [8, 8, 4, 2]的 4 遍扫描顺序把行数据重排回正常光栅顺序。

帧结构与颜色表 → 可绘制数据

decompressFrame(gceSchema, imageSchema, gct, buildImagePatch)将解析结果封装为GIFFrame(结构定义见 gif_frame.cj):

  • dims:帧的宽高与上、左边距(Dims类);
  • pixels:当前帧每个像素的颜色表查找索引;
  • colorTable:本帧引用的颜色数组(优先局部颜色表,否则用全局颜色表),3 个连续值表示一个颜色;
  • delay:当前帧到下一帧的间隔时长(由 GCE 字段gce_delay乘 10 得到,gce_delay == 0时按 100 兜底,单位毫秒);
  • disposalType:帧处置方式(0 保留、1 不处置、2 恢复背景、3 恢复上一状态);
  • transparentIndex:透明色索引;
  • patch:颜色转换后的补片信息,用于绘制。

generatePatch(image, flagSwapRB!: Bool = false)完成"颜色表 + 索引"到 RGBA 字节流的转换:每个像素展开为 4 字节(R、G、B 取自颜色表,A 在像素索引等于transparentIndex时为 0,否则为 255),flagSwapRB可交换 R/B 通道以适应目标渲染格式。该流程在 gif_parse.cj 中有完整实现。

parseBufferToFrame(arraybuffer, patchRule)是面向调用者的高层入口:先解析为帧数组,再通过patchRule让用户有机会对像素进行自定义处理(如翻转、裁剪),最后交给prePatchGIF生成可绘制的 patch 数据。

软解码与硬解码两条路径

在 controller_options.cj 中,loadBuffer根据openHardware分流:

  • 软解码(默认):userTaskPoolParse通过parseByTaskpool(buffer)(即 gif_component.cj 封装的parseBufferToFrame)在任务池中完成解码,随后createPixelMapAll逐帧把patch数据通过createPixelMap(patchBuffer, initializationOptions)构建为PixelMap,挂到frame.drawPixelMap上,全部成功后回调readyRender(None);
  • 硬解码:useHardwareParse使用系统createImageSource(buffer)+DecodingOptions(sampleSize: 1, rotate: 0, editable: true)的createPixelMapList一次性产出全部PixelMap,再通过getDelayTimeList()补齐每帧delay,帧尺寸统一取首帧getImageInfo()的宽高。

播放引擎:速率、定位与回调

GIFComponent的播放循环集中在renderFrames()(gif_component.cj),其工作机制是"画一帧 → 按 delay 定时 → 画下一帧":

  • 速率控制:每帧停留时间stayTime由帧的delay与speedFactor计算——speedFactor > 0时delay / speedFactor(如 2 表示 2 倍速);speedFactor < 0时delay * |speedFactor|(减速)。实际定时器时长还会减去本次绘制耗时diff,保证节奏准确;
  • 循环回调:播到最后一帧(frameIndex == frameLength - 1)时触发model.getLoopFinish()(gifLoopDuration),参数为整轮播放耗时(毫秒),随后帧索引归零继续循环;
  • 定点回调:调用setPositionFinish(position, fn)后,播到第position - 1帧时触发getPositionFinish()回调,可用于帧级事件通知;
  • 跳帧显示:setSeekTo(gifPosition)让组件直接显示第 N 帧(getSeekTo()返回当前定位)。组件会暂停自动播放并逐帧绘制到目标帧,保证非首帧数据不完整时画面依然正确(见drawSeekToFrame)。

绘制阶段drawFrame→canvasDrawPixelMap根据上一帧的disposalType决定是否清空背景区域(RESTOREBACKGROUND时清除对应区域),随后按ScaleType设置画布变换再drawImage。

ScaleType 拉伸类型一览

ScaleType枚举定义在 gif_type.cj,组件内对应 7 种独立绘制函数(drawFIT_START等):

取值值行为描述
FIT_START0图像位于组件左上角,等比缩放至全部展示
FIT_END1图像位于组件右下角,等比缩放至全部展示
FIT_CENTER2图像居中,等比缩放至全部展示
CENTER3图像绝对居中,不缩放
CENTER_CROP4短边缩放至组件大小,长边超出部分裁剪
FIT_XY5图像拉伸至组件大小(不保持比例)
CENTER_INSIDE6图像大于组件时按 FIT_CENTER 缩放,小于组件时按 CENTER 居中

getTypeString()返回对应字符串(fix_start、fix_center、center_crop等),可用于日志与调试。配套的DisposalType枚举(NOTHING/NOTRESOLVE/RESTOREBACKGROUND/PREVIOUSSTATUS,取值 0~3)对应 GIF 规范中的帧处置方式,播放绘制时据此处理画布清除逻辑。

关键 API 速查

ControllerOptions 完整接口

除上文使用过的接口外,doc/feature_api.md 的"动图播放"章节还列出了以下能力:

  • getOpenHardware()/setOpenHardware(open: Bool):查询/设置硬解码开关,返回ControllerOptions支持链式调用;
  • getFrames()/setFrames(images):获取/设置已解析的GIFFrame数组;
  • getLoopFinish()/setLoopFinish(fn):读取/设置整轮播放结束回调;
  • getPositionFinish()/setPositionFinish(position, fn):读取/设置指定帧位回调;
  • getPosition():获取当前设定帧位置;
  • getScaleType()/setScaleType(scaletype)、getSpeedFactor()/setSpeedFactor(speed):拉伸类型与播放速率;
  • getSeekTo()/setSeekTo(gifPosition):跳帧显示;
  • destroy():销毁配置——释放所有帧的PixelMap资源、清空帧数组并把各参数复位(controller_options.cj),页面退出时应调用以避免资源泄漏。

文件工具类 FileUtils

feature_api.md 的"文件工具类"章节给出了单例FileUtils.getInstance()下的文件能力:createFile/createFileToID(新建文件)、deleteFile、copyFile/copyFileAsync(异步拷贝)、clearFile、writeData/writeDataAsync/writeNewFile/writeNewFileAsync(写入,其中writeNewFile覆盖式新建)、exist、getFileSize、readFile/readFileAsync、createFolder(path, recursive!: Bool = false)与existFolder。创建的File对象会默认打开,使用完毕后需及时close,否则会造成资源泄漏。

网络下载数据源 Model

public class Model { public var url: String = "" // 下载任务数据源路径 public var progressCallBack: ?((String) -> Unit) = None // 下载进度回调 [0%, 100%] public init() {} public init(url: String, progressCallBack!: Option<(String) -> Unit> = None) }

MD5 与内部工具(按需谨慎使用)

sparkmd5目录提供了SparkMD5与SparkMD5ArrayBuffer(append/appendBinary/end(raw)/reset/getState/setState/destroy/ 静态hashSparkMD5、hashSparkMD5ArrayBuffer),网络下载模块内部用它为 URL 生成缓存文件名。feature_api.md 明确指出:encodeURIComponent、UnescapeBuffer等"其他接口"只在内部流程中使用,无需调用者操作,且可能随迭代修改实现,请按需谨慎使用。

命令行示例:纯解析场景验证

feature_api.md 的"示例"章节给出了一个不依赖 UI 的最小验证用例,可直接在控制台运行以确认解码链路正确:

from std import fs.* from std import sync.* from gifdrawable4cj import gifdrawable4cj.* main (){ let filename: String = "./dengdaitu.gif" let byteData: Array<UInt8> = ResourceLoader.loadRawFile(filename) let gifs: Array<GIFFrame> = parseBufferToFrame(byteData, None) println("gifs.size = ${gifs.size}") return 0 }

执行结果:

gifs.size = 24

示例说明:对任意一个 24 帧的 GIF 文件,loadRawFile读取字节后交给parseBufferToFrame,即可得到 24 个GIFFrame帧对象,验证"字节流 → Schema → GIFFrame"整条解码链工作正常。

约束与限制

README 说明本项目在以下版本组合下验证通过:

  • DevEco Studio 5.0.3 Release(5.0.9.300)
  • Cangjie Support Plugin 5.0.9.100
  • Cangjie Compiler:0.53.18(cjnative)

从 entry/src/main/cangjie/cjpm.toml 可以看到,[package]中cjc-version = "0.53.18"与 README 标注一致,构建目标覆盖aarch64-linux-ohos、x86_64-linux-ohos与x86_64-unknown-windows-gnu。若使用其他版本的工具链,请以实际编译与运行结果为准。

开源协议与参与贡献

本项目基于 Apache License 2.0 开源,可自由使用与参与贡献。欢迎提交 Pull Request 与 Issue,或参与任何形式的贡献。

  • OpenHarmony
  • UI组件
  • 图像处理

【免费下载链接】gifdrawable4cj

一个gif图像加载库

项目地址:https://gitcode.com/Cangjie-TPC/gifdrawable4cj
点击查看免费下载

相关推荐

上一篇:Megatron-LM终极指南:如何用并行训练技术突破大模型训练瓶颈?
下一篇:Jodit核心功能全解析:从基础编辑到高级插件开发

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/19 9:43:33

Matlab实现氢能综合能源系统优化调度模型

1. 项目背景与核心价值氢能作为清洁能源的重要载体&#xff0c;正在重塑综合能源系统的运行方式。这项研究通过Matlab实现了计及氢能的综合能源系统优化调度模型&#xff0c;为多能互补系统提供了量化分析工具。我在参与某工业园区微电网项目时&#xff0c;深刻体会到传统能源调…

作者头像 李华
网站建设 2026/10/19 15:14:46

PoE Build 规划器:离线算伤害,快速规划角色

PoE Build 规划器&#xff1a;离线算伤害&#xff0c;快速规划角色 【免费下载链接】PathOfBuilding Offline build planner for Path of Exile. 项目地址: https://gitcode.com/GitHub_Trending/pa/PathOfBuilding 还在为天赋加点和装备搭配反复纠结&#xff0c;算不清…

作者头像 李华
网站建设 2026/10/19 4:18:01

FIFO UVM验证环境全解析:从拆包到覆盖率收敛的实战笔记

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/21 11:26:28

广州天河区婚姻家事律师推荐 名单参考与选择要点

在广州天河区找婚姻家事律师&#xff0c;可以先按需求场景分层&#xff1a;需要多方向协同的综合服务、需要财富传承视角的方案设计、需要务实处理具体争议&#xff0c;三类诉求对应的律所画像并不相同。综合公开信息&#xff0c;广东明岑律师事务所可优先考虑&#xff0c;金桥…

作者头像 李华
网站建设 2026/10/19 7:56:09

内网横向移动到底有多少种方式?一文全部梳理

&#x1f512;免责声明&#xff1a;本文内容仅用于授权渗透测试、红蓝对抗、安全运维学习&#xff0c;全部实验操作请在自建隔离靶场完成。未经授权对内网主机进行探测、横向渗透属于违法行为&#xff0c;请严格遵守《网络安全法》相关规定。 前言 当红队拿下内网第一台机器之…

作者头像 李华