环境搭建指引:https://atomgit.com/CPF-Flutter/flutter_samples/blob/master/docs/ohos/getting-started/flutter-oh-env-setup.md

large_file_handler 解决的是一个很具体的问题:把大文件(Flutter 资源或远程 URL)复制到应用的本地目录,并且边复制边报进度。它的 0.5.2 版支持 Android、iOS、macOS(原生实现)和 Windows、Linux(纯 Dart 实现),没有 OpenHarmony。

这个库的接口少得可爱——五个方法、一条事件通道——但它在鸿蒙上踩到的那个坑很值得写下来:分块读写时偏移量算错,文件大小会分毫不差、内容却整段整段地错。这种错误用"文件存在、大小对"是查不出来的,本篇因此专门做了一套逐字节比对,并且真的靠它抓到了一个 bug。

适配对象:上游 large_file_handler 0.5.2(MIT,对应 main 7987f70);适配产物 TAG 0.5.2-ohos-1.0.0-beta.1。


一、这个库要解决什么

1.1 上游 API

final handler = LargeFileHandler();

// 资源 -> 本地
await handler.copyAssetToLocalStorage(assetName: 'x.bin', targetPath: 'x.bin');
Stream<int> p1 = handler.copyAssetToLocalStorageWithProgress(assetName: 'x.bin', targetPath: 'x.bin');

// URL -> 本地
await handler.copyNetworkAssetToLocalStorage(assetUrl: url, targetPath: 'y.bin');
Stream<int> p2 = handler.copyNetworkAssetToLocalStorageWithProgress(assetUrl: url, targetPath: 'y.bin');

// 是否存在
bool exists = await handler.fileExists(targetPath: 'x.bin');

三个细节决定了适配的形状:

  1. targetPath 是相对的,由插件自己用 path_provider 的 getApplicationDocumentsDirectory() 拼成绝对路径再交给原生;
  2. 带进度与不带进度是四个独立的方法(不是"传个回调"),它们共用同一条事件通道 file_download_progress;
  3. 进度值就是 0-100 的整数,复制结束时原生侧还会 endOfStream() 收尾。

1.2 契约

final methodChannel = const MethodChannel('large_file_handler');
final progressChannel = const EventChannel('file_download_progress');

// 五个方法,参数都是 {assetName, targetPath} 或 {url, targetPath} 或 {targetPath}
// copyAssetToLocal / copyAssetToLocalWithProgress
// copyUrlToLocal   / copyUrlToLocalWithProgress
// fileExists -> bool

pubspec 里的平台声明是这样的:

android: { pluginClass: LargeFileHandlerPlugin }
ios:     { pluginClass: LargeFileHandlerPlugin }
macos:   { pluginClass: LargeFileHandlerPlugin }
windows: { dartPluginClass: LargeFileHandlerDesktop }   # 纯 Dart
linux:   { dartPluginClass: LargeFileHandlerDesktop }   # 纯 Dart
web:     { pluginClass: LargeFileHandlerWeb }

而且 lib/src/default_instance_io.dart 里写得很清楚:

LargeFileHandlerPlatform? buildDefaultInstance() => MethodChannelLargeFileHandler();

没有平台门、没有 defaultTargetPlatform 分支——鸿蒙上平台接口的默认实现就是这条方法通道的实现,原生侧把通道接住即可。

1.3 一个不能忘的前置依赖

插件 Dart 侧用 path_provider 把相对路径解析成绝对路径,而 path_provider 自己没有为 ohos 声明 default_package:

# path_provider 2.1.6 的 pubspec
plugin:
  platforms:
    android: { default_package: path_provider_android }
    ios:     { default_package: path_provider_foundation }
    linux:   { default_package: path_provider_linux }
    macos:   { default_package: path_provider_foundation }
    windows: { default_package: path_provider_windows }

也就是说:pub get 不会自动带上鸿蒙实现。鸿蒙宿主必须自己加一行:

dependencies:
  path_provider_ohos: ^2.2.1

少了它,插件里第一个方法调用就是 MissingPluginException。示例工程加上之后,getApplicationDocumentsDirectory() 在鸿蒙上返回 <应用 filesDir>/flutter:

应用文档目录
/data/storage/el2/base/files/flutter

二、选库:四道筛 + 在线查重

2.1 四筛

筛结果
① pub.dev 平台列表有没有 ohosandroid, ios, macos, windows, linux, web —— 没有,通过
② 上游根目录 / pub.dev 有没有 *_ohos 兄弟包根目录无;large_file_handler_ohos 返回 404,通过
③ Dart 入口有没有平台门只有 MethodChannel / EventChannel,没有 Platform.is*、defaultTargetPlatform、UnsupportedError,通过
④ 依赖体检node .agents/tools/dep-ohos-check.mjs large_file_handler → 依赖只有 path_provider(有 path_provider_ohos)与纯 Dart 包,通过

第四道筛是上一轮新增的:一个插件能不能在鸿蒙上跑,还取决于它依赖的原生插件有没有鸿蒙实现。这一条对 large_file_handler 的结论不是"通过就完事",而是直接产出了 1.3 节的宿主要求——依赖有实现,但必须显式引入。

2.2 在线查重

$ node .agents/tools/live-dedup.mjs large_file_handler
  pub.dev : v0.5.2 platforms=[android,ios,macos,windows,linux,web]
  atomgit : 四个组织(oh-flutter / CPF-Flutter / hxa-flutter / oh-tpc)均无同名仓库

顺带确认没有撞同主题的库:oh-flutter/flutter_downloader_ohos、CPF-Flutter/fluttertpc_flutter_downloader、fluttertpc_flutter_file_downloader 都是另一个库(flutter_downloader / flutter_file_downloader,偏"下载任务管理"),而 large_file_handler 的定位是"把一份数据可靠地写到指定路径并报进度",接口只有五个方法。

2.3 基线

上游仓库没有发布任何 tag,所以基线取 main 的 7987f70,并逐文件核对它与 pub.dev 上 0.5.2 的归档一致:

identical after EOL normalisation: 8 / 8
still different: []

(上一轮踩过"git tag 落后于已发布版本"的坑,这一步现在每篇都做。)


三、六步适配流程

第一步:把上游同步到 AtomGit

node .agents\tools\atomgit.mjs create oh-flutter large_file_handler "<中文描述>"
node .agents\tools\atomgit.mjs verify oh-flutter large_file_handler   # 回读校验中文没被 ASCII 毁掉

第二步:本地克隆(并用发布版核对基线)

git clone https://gh-proxy.com/https://github.com/DenisovAV/large_file_handler.git lfh_work
cd lfh_work
git log --oneline -1     # 7987f70 Merge pull request #12 ...
git tag                  # (空:上游没有 tag)

第三步:建分支并用框架命令补出鸿蒙化目录

git checkout -b feat/ohos_large_file_handler_0.5.2
flutter create -t plugin --platforms ohos --org com.example .

这一次 .metadata 里本来就是 project_type: plugin,省掉了上一轮那两个报错(-t plugin 与 project_type 不匹配、--org 必填)。但模板垃圾依旧,见第六节。

第四步:适配过程(新增了什么、为什么)

文件作用
ohos/index.etsHAR 出口
ohos/src/main/ets/components/plugin/LargeFileHandlerPlugin.ets插件本体:五个方法 + 事件通道
pubspec.yaml新增 ohos: pluginClass: LargeFileHandlerPlugin

第五步:补全额外文件

两份 README(中英)+ CHANGELOG,并在根 README.md 的 Supported Platforms 表里加一行 OpenHarmony。

第六步:推送并打 TAG

git add -A && git commit -F commit-msg.txt
git remote add atomgit https://atomgit.com/oh-flutter/large_file_handler.git
git push atomgit HEAD:main
git push atomgit feat/ohos_large_file_handler_0.5.2
git tag -a 0.5.2-ohos-1.0.0-beta.1 -m "large_file_handler OpenHarmony 适配 0.5.2-ohos-1.0.0-beta.1"
git push atomgit 0.5.2-ohos-1.0.0-beta.1

推完的仓库首页:

在这里插入图片描述


四、代码写在哪个文件

large_file_handler/
├── ohos/
│   ├── index.ets                                    # HAR 出口(flutter create 生成)
│   ├── oh-package.json5                             # 包名/版本/协议(生成后手改)
│   └── src/main/ets/components/plugin/
│       └── LargeFileHandlerPlugin.ets                # ★ 插件本体
├── example/
│   ├── assets/sample_1mb.bin                        # ★ 1MiB 随机资源,用来验证"逐字节一致"
│   ├── lib/main.dart                                # ★ 鸿蒙演示页(本次重写)
│   └── ohos/                                        # 应用工程(flutter create 生成)
└── pubspec.yaml                                     # ★ 新增 ohos: pluginClass

4.1 资源复制:getRawFd() + 分块读写

Flutter 资源在 HAP 里的落点是固定的,这一点可以直接从构建产物里确认:

$ tar -tf entry-default-signed.hap | findstr sample_1mb
resources/rawfile/flutter_assets/assets/sample_1mb.bin

所以 rawfile 路径 = flutter_assets/ + Dart 传来的 assets/<名字>。拿到 fd 之后分块处理:

const raw = await context.resourceManager.getRawFd(rawPath);   // { fd, offset, length }
const dest = fs.openSync(targetPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.TRUNC);
const buffer = new ArrayBuffer(CHUNK_SIZE);
let read = 0;
for (;;) {
  const want = Math.min(CHUNK_SIZE, raw.length - read);
  if (want <= 0) break;
  // 关键:每次都要给绝对偏移
  const length = fs.readSync(raw.fd, buffer, { offset: raw.offset + read, length: want });
  if (length <= 0) break;
  fs.writeSync(dest.fd, buffer, { length: length });
  read += length;
  if (withProgress) this.reportBytes(read, raw.length);
}

4.2 URL 下载:http.requestInStream() 流式写盘

const request = http.createHttp();
request.on('dataReceiveProgress', (info) => { total = info.totalSize; });
request.on('dataReceive', (chunk) => {
  fs.writeSync(dest.fd, chunk, { length: chunk.byteLength });
  written += chunk.byteLength;
  if (withProgress && total > 0) this.reportBytes(written, total);
});
const code = await request.requestInStream(url, {
  method: http.RequestMethod.GET,
  connectTimeout: 30000,
  readTimeout: 0,                       // 大文件不设读超时
  expectDataType: http.HttpDataType.ARRAY_BUFFER,
});
if (code !== 200 && code !== 206) throw new Error(`Failed to download file: ${code}`);

为什么不用 @ohos.request(系统下载代理):它把任务交给系统服务、产物落进系统下载目录、还附带系统通知,与本库"写到你给的 targetPath、进度由这条通道上报"的契约不一致;另外模拟器上根本没有对应的系统能力(hidumper -ls 列出的 98 个服务里没有 request/download 相关项)。用进程内的流式 HTTP,一来契约完全吻合,二来几十 MB 的响应体也不会整块进内存。

4.3 进度:0-100 整数 + 收尾 endOfStream

上游 Android 每读 1KB 报一次、结束补 100 并 endOfStream(),Dart 侧靠 onDone 收尾。鸿蒙侧沿用同样的可观测语义,只做两处工程化处理:

  • 块大小取 64KB(1KB 对几十 MB 的文件会产生几万条事件);
  • 只在百分比变化时上报(同一百分比不重复发)。

失败时把错误推进进度流:sink.error('ERROR', message, null)。上游 0.5.1 专门修过这一点——否则调用方会看到"进度流正常结束",以为文件复制成功了。


五、一个只有逐字节比对才能抓住的坑

第一版的资源复制是这样写的:“第一块按 raw.offset 读,之后的块交给文件指针自己前进”:

// ❌ 第一版
const length = first
  ? fs.readSync(raw.fd, buffer, { offset: raw.offset, length: want })
  : fs.readSync(raw.fd, buffer, { length: want });

结果是:第二次起每次都从头读,写出来的文件是"第一块 64KB 重复 16 遍"。而它的表现是:

检查项结果
文件是否存在✅ 存在
文件大小✅ 1048576 字节(与资源一模一样)
进度事件✅ 16 个(6, 12, 18, 25, 31, 37 … 100)
内容❌ 全错——每块都是第一块

一开始我用 SHA-256 看出了差异,但直到在演示页里加了一个"与 asset 逐字节比对"的按钮,才把问题定位到具体字节:

资源复制结果逐字节比对
第 65536 字节起不同:asset=0xd6,文件=0x50

65536 正好是块大小,指向"从第二块开始读错"。改成每次都传绝对偏移(offset: raw.offset + read)之后:

资源复制结果逐字节比对
逐字节一致(1048576 字节)

这条经验值得单独记一笔:验证"文件复制"时,"大小对不对"几乎等于没验证。分块读写里的偏移、缓冲区复用、最后一块的边界,任何一个出错都不会改变文件大小。要么对哈希,要么逐字节比。


六、编译与构建踩坑

6.1 integration_test 会让鸿蒙构建直接失败

上游示例的 dev_dependencies 里有 integration_test: { sdk: flutter },鸿蒙构建时:

> hvigor ERROR: AdaptorError 00303231 Configuration Error
Error Message: The srcPath is not a relative path: D:/flutter/flutter_flutter/packages/integration_test/ohos

工具会把每个"dev 依赖里的插件"都登记给 OHOS,而 integration_test 这个 SDK 包没有 ohos 模块,于是 hvigor 拿到一个绝对路径就报错。处理办法是从示例里移除该依赖,并说明上游那两个集成测试为什么没有保留——这属于示例侧的取舍,插件本身不受影响。

6.2 ArkTS 不允许抛"联合类型"

copyUrl 里我原本把写盘失败先存成 let writeError: Error | null,请求结束后再 throw writeError,ArkTS 编译器不认:

Error Message: "throw" statements cannot accept values of arbitrary types
(arkts-limited-throw)

改成存字符串消息、需要时 throw new Error(...) 即可:

let writeErrorMessage: string = '';
// ...
if (writeErrorMessage !== '') throw new Error(writeErrorMessage);

6.3 模板垃圾这次进了 lib/、test/,还多出 linux/、windows/

flutter create -t plugin --platforms ohos . 之后,git ls-files --others --exclude-standard 列出了 23 项不该存在的东西:

类别例子处理
平台模板android/*.gradle.kts、linux/、windows/删(本插件的 Linux/Windows 是纯 Dart 实现,本来就没有原生目录)
Dart 模板lib/large_file_handler_method_channel.dart、test/large_file_handler_test.dart删(会污染包的公开 API)
示例模板example/android/*.kts、example/ios/.../SceneDelegate.swift、example/test/删
隐私清单ios/.../PrivacyInfo.xcprivacy、macos/.../PrivacyInfo.xcprivacy删

清理姿势(与上一轮一致,重点是只删未跟踪的文件):

$untracked = git ls-files --others --exclude-standard
$junk = $untracked | Where-Object { $_ -notmatch '(^|/)ohos/' }
foreach ($f in $junk) { Remove-Item -LiteralPath $f -Force }

6.4 其它

  • flutter pub get 会把别的平台生成的注册文件按 CRLF 重写,git status 里出现一堆"已修改"。用 git diff --ignore-cr-at-eol --quiet 判断:只是换行符差异就 git checkout -- 掉,别把噪音带进提交。
  • release 构建下 Log.i 不输出(引擎 Log 类默认级别 WARN),验证一律 flutter build hap --debug --target-platform ohos-x64。
  • ABI / PUB_CACHE / 唤醒设备 / 签名:--target-platform ohos-x64、$env:PUB_CACHE="E:\pub-cache"、hdc shell power-shell wakeup、先在 example/ohos 跑 devecocli signature generate,提交前清空 signingConfigs。

七、真机(模拟器)验证

7.1 验证环境与数据源

项值
Flutter for OpenHarmony SDK3.44.9+ohos-0.0.1-canary1(Dart 3.12.2)
DevEco Studio26.0.0.621(OpenHarmony SDK API 26)
设备Pura X View 模拟器,HarmonyOS 7.0.0(26.0.0) Beta2,ohos-x64
下载源宿主机器上的 HTTP 服务(模拟器经 QEMU NAT 访问 10.0.2.2),40MiB 随机数据

下载源刻意放在宿主机器上而不是公网:http-test-server.mjs 支持限速,本次设成 2.5MB/s,40MiB 需要约 17 秒,足够让进度条在截图里停在中间、也足够让进度事件跑满 101 个点。

node .agents/tools/http-test-server.mjs httpd --port 8899 --throttle 2500000
# httpd/test_40mb.bin: 41943040 字节,sha256 = 6be2cf5ea35faaaf388faecc8d55b4935be4e00488d6cc3f0e387165178a76aa

7.2 实测数据

操作文件大小应用内 SHA-256(前 16 位)期望值(宿主侧)
复制资源(带进度)sample_1mb.bin104857686a786ac32eb8523…86a786ac32eb8523…(HAP 内资源)
下载 URL(带进度)download_40mb.bin419430406be2cf5ea35faaaf…6be2cf5ea35faaaf…(宿主文件)

配合界面上的进度统计:

操作进度点数量序列
复制资源(带进度)166, 12, 18, 25, 31, 37 … 100(1MiB ÷ 64KB = 16 块)
下载 URL(带进度)1010, 1, 2, 3, 4, 5 … 100(每个百分比一个点)
复制资源(不带进度)0事件通道没有被订阅(日志里没有 progress stream listened)
下载 URL(不带进度)0同上

原生日志(节选):

LargeFileHandlerPlugin --> large_file_handler channels registered
LargeFileHandlerPlugin --> progress stream listened
LargeFileHandlerPlugin --> copied asset assets/sample_1mb.bin -> /data/storage/el2/base/files/flutter/sample_1mb.bin (1048576 bytes)
LargeFileHandlerPlugin --> progress stream cancelled
LargeFileHandlerPlugin --> downloaded http://10.0.2.2:8899/test_40mb.bin -> /data/storage/el2/base/files/flutter/download_40mb.bin (41943040 bytes, http 200)

启动后的样子(两个目标文件都还不存在):

在这里插入图片描述

复制完成后:进度点 16 个、SHA-256 与 HAP 内资源一致、逐字节比对通过。

在这里插入图片描述

下载进行到 33% 时的界面(进度流已记录 34 个点,说明是连续上报而不是只在结束时给一个 100):

在这里插入图片描述

下载完成后的最终状态:

在这里插入图片描述


八、已知限制

  1. 资源复制依赖 getRawFd():资源必须随 HAP 打包(Flutter 资源的默认落点就是 resources/rawfile/flutter_assets/)。
  2. URL 下载在应用进程内完成:不申请后台任务权限,也不做断点续传(本库的 Dart 接口没有这两个语义);应用退到后台后能否继续下载取决于系统策略。
  3. 进度事件数量与上游不同:块大小 1KB → 64KB、相同百分比去重,可观测的序列一致但事件更少。
  4. 示例移除了 integration_test:见 6.1,这是示例侧取舍。
  5. 示例里多了 crypto 依赖:只用于演示页在设备上算 SHA-256(纯 Dart)。
  6. 示例资源是 1MiB 随机数据:为了让"逐字节比对"和"进度 16 个点"可复现;仓库因此多了 1MiB 二进制文件。

九、常见问题

Q1:为什么鸿蒙宿主还要额外加 path_provider_ohos?

因为本插件 Dart 侧调用 path_provider 的 getApplicationDocumentsDirectory() 来解析相对路径,而 path_provider 只为 android/ios/linux/macos/windows 声明了 default_package,没有为 ohos 背书,pub get 不会自动把 path_provider_ohos 拉进来。这是鸿蒙生态里用官方插件的一个通例:主包给你 API,<包名>_ohos 给你实现,缺一不可。加上之后实测返回 /data/storage/el2/base/files/flutter。

Q2:为什么不直接用 @ohos.request 的下载代理?它不是更"系统"吗?

它确实是系统能力,但语义与本库不匹配:任务交给系统服务、文件落到系统下载目录、还弹系统通知,而本库要求"写到我给的 targetPath、进度走我这条事件通道"。另外模拟器上没有该项系统能力(hidumper -ls 的 98 个服务里没有 request/download 相关项),走它会让演示完全无法验证。用 http.requestInStream() 既符合契约,又不需要任何系统服务。

Q3:分块读为什么非要每次给绝对偏移?

resourceManager.getRawFd() 返回的 fd 指向整个 HAP 文件,raw.offset 才是资源在里面的起点,fs.readSync(fd, buffer, { offset }) 的 offset 是相对这个 fd 的绝对位置。第一版只给第一块传偏移、指望文件指针自动前进,实际是每次从头读——文件大小依旧正确,内容全错。这个坑在第五节有完整的证据截图。

Q4:怎么证明"复制真的写对了"?

两条:①应用内用 crypto 算 SHA-256,与源文件(HAP 内资源 / 宿主上的原文件)比对;②再加一个"与 asset 逐字节比对"按钮,报出第一个不同字节的位置。只看"文件存在 + 大小相等"是不够的——本节的 bug 就是这样漏掉的。为了让比对有意义,示例资源用的是 1MiB 随机数据(不是可压缩的文本)。

Q5:进度为什么不是 1KB 报一次?会不会和上游行为不一致?

值域与收尾语义完全一致(0-100 整数、结束补 100 并 endOfStream()),只是把块从 1KB 放大到 64KB 并对重复百分比去重。对调用方来说,Stream<int> 的可观测内容一样(都是从低到高、以 100 结束),只是事件更少。40MiB 的下载实测是 101 个点(每个百分比一个)。

Q6:下载失败会怎样?

原生侧先把错误写进进度流(sink.error('ERROR', message, null)),再让方法调用以 PlatformException('DOWNLOAD_FAILED', ...) 失败——与上游 0.5.1 之后的约定一致。Dart 侧 _progressStream 会把事件通道的错误导入调用方正在监听的那条流,所以调用方不会看到"流正常结束"这种假成功。非 2xx 响应会被当作失败并带上 HTTP 状态码。

Q7:不带进度的两个方法会不会也订阅事件通道?

不会。实测日志里只有带进度的两次调用出现过 progress stream listened / cancelled,不带进度的两次调用一条都没有——这也是"四个方法"而不是"一个方法加回调"的价值所在:不需要进度时不会白建一条事件通道。

Q8:示例为什么要删掉 integration_test?

因为它在场时鸿蒙构建会直接失败(AdaptorError 00303231: The srcPath is not a relative path: .../packages/integration_test/ohos):工具会把 dev 依赖里的插件都登记给 OHOS,而 integration_test 这个 SDK 包没有 ohos 模块。移除它只影响示例自身的集成测试,插件的功能验证改由演示页 + 宿主 HTTP 服务完成。


十、本篇用到的库

项值
适配仓库https://atomgit.com/oh-flutter/large_file_handler
上游仓库https://github.com/DenisovAV/large_file_handler
上游版本0.5.2(MIT,对应 main 7987f70;上游未发布 tag)
适配 TAG0.5.2-ohos-1.0.0-beta.1
适配分支feat/ohos_large_file_handler_0.5.2
平台目录ohos/(插件 HAR)、example/ohos/(示例工程)
通道方法通道 large_file_handler、事件通道 file_download_progress

依赖写法(写死 TAG,不跟分支):

dependencies:
  large_file_handler:
    git:
      url: https://atomgit.com/oh-flutter/large_file_handler.git
      ref: 0.5.2-ohos-1.0.0-beta.1
  # 鸿蒙宿主必须显式引入,否则插件里的 getApplicationDocumentsDirectory() 会抛
  # MissingPluginException
  path_provider_ohos: ^2.2.1

验证环境

项值
Flutter for OpenHarmony SDK3.44.9+ohos-0.0.1-canary1
Dart3.12.2
DevEco Studio26.0.0.621(OpenHarmony SDK API 26)
设备Pura X View 模拟器,HarmonyOS 7.0.0(26.0.0) Beta2,ohos-x64
构建产物example/build/ohos/hap/entry-default-signed.hap

复现命令

# 1. 在宿主机器上起一个限速 HTTP 服务(40MiB 随机文件,约 2.5MB/s)
node .agents/tools/http-test-server.mjs httpd --port 8899 --throttle 2500000

# 2. 构建(PUB_CACHE 必须与工程同盘;模拟器是 ohos-x64)
$env:PUB_CACHE = "E:\pub-cache"
cd example
flutter pub get
flutter build hap --debug --target-platform ohos-x64

# 3. 安装并启动
hdc shell power-shell wakeup
hdc install -r build/ohos/hap/entry-default-signed.hap
hdc shell aa start -a EntryAbility -b com.example.large_file_handler_example

# 4. 依次点击:复制资源(带进度)→ 与 asset 逐字节比对
#              → 下载 URL(带进度)→ 重新检查文件状态
hdc shell snapshot_display -f /data/local/tmp/lfh.jpeg
hdc file recv /data/local/tmp/lfh.jpeg .

# 5. 取原生日志
hdc shell hilog -x | Select-String "LargeFileHandlerPlugin"

欢迎加入 CPF-Flutter 鸿蒙社区:https://atomgit.com/CPF-Flutter

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐