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

鸿蒙ArkTs如何实现pdf预览功能?

鸿蒙ArkTs如何实现pdf预览功能?

    • 前言
    • PDFKit运行示例代码报错
    • 真机运行
    • 先看效果
    • 一、预览本地pdf文件
    • 二、预览线上的pdf文件
    • 三、预览沙箱目录中pdf的文件(重点)
    • 效果中的整体代码
    • 总结
  • Harmony OS NEXT版本(接口及解决方案兼容API12版本或以上版本)

前言

在开发鸿蒙App时,你是否做过pdf预览功能。是否也和我一样碰壁了,来看看我遇到的问题,以及我是如何实现的吧。

PDFKit运行示例代码报错

the requested module '@hms:officeservice.PdfView' does not provide an export name 'pdfViewManager' which imported by 'xxxx'

真机运行

本来以为用真机就能运行了,没想到还是报错
在这里插入图片描述
那么下面来看看我是如何实现的吧

先看效果

在这里插入图片描述
视频转完gif,视觉上看起来有点卡,实际运行不卡。

一、预览本地pdf文件

预览本地的pdf文件很简单,使用Web组件加载即可。
pdf文件目录:harmonyApp\entry\src\main\resources\rawfile\test.pdf
具体代码如下:

import web_webview from '@ohos.web.webview';@Entry
@Component
struct Index {webviewController: web_webview.WebviewController = new web_webview.WebviewController();build() {Column() {// src-本地pdf文件Web({ src: $rawfile('test.pdf'), controller: this.webviewController }).layoutWeight(1).domStorageAccess(true)}.height('100%')}
}

二、预览线上的pdf文件

这里的线上的pdf文件是指可以在浏览器直接打开预览的pdf文件,还有一种是在浏览器打开是直接进入下载的,那么就需要我们进一步处理了,第三点有详解。
这样的文件预览也很简单,使用Web组件加载即可。
具体代码如下:

import web_webview from '@ohos.web.webview';@Entry
@Component
struct Index {webviewController: web_webview.WebviewController = new web_webview.WebviewController();build() {Column() {// 线上pdf链接Web({ src: 'http://www.cztouch.com/upfiles/soft/testpdf.pdf', controller: this.webviewController }).layoutWeight(1).domStorageAccess(true)}.height('100%')}
}

三、预览沙箱目录中pdf的文件(重点)

这种就比较麻烦了,有的pdf链接在浏览器打开直接跳转下载不会预览,那么就需要我们下载到沙箱目录中,再预览沙箱目录中的pdf文件。
我这里用到了一个pdfviewer工具,可从我的百度网盘免费获取
拿到文件夹后,放在以下目录:
项目目录:harmonyApp\entry\src\main\resources\rawfile
具体实现代码如下:

import router from '@ohos.router';
import web_webview from '@ohos.web.webview';
import { BusinessError, request } from '@kit.BasicServicesKit';
import showToast from '../../common/utils/ToastUtils';
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { util } from '@kit.ArkTS';interface IBase64 {base64: string;fileName: string;
}@Entry
@Component
struct Index2 {controller: web_webview.WebviewController = new web_webview.WebviewController()// pdf文件路径@State fileUrl: string = ''// 本地沙箱文件地址@State tempFilePath: string = ''// 是否显示按钮@State isShowBtn: boolean = true;build() {Stack() {Column() {// 页面内容Scroll(){Column(){if(this.tempFilePath){if(this.isShowBtn){Button('打开文件').onClick(()=>{this.isShowBtn = false;})}else{Web({ src: $rawfile('pdfviewer/viewer.html'), controller: this.controller }).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)}).domStorageAccess(true) // 设置是否开启文档对象模型存储接口(DOM Storage API)权限,默认未开启。.onPageEnd(()=>{let file = this.sandBoxPdfToBase64(this.tempFilePath);this.controller.runJavaScript(`openFile("${file.base64}", "${file.fileName}")`);})}}}.width('100%').height('100%')}.edgeEffect(EdgeEffect.Fade).width('100%').layoutWeight(1).align(Alignment.TopStart)}.height('100%').backgroundColor(Color.White)}}// 沙箱pdf文件转base64方法sandBoxPdfToBase64(url: string) {let file = fs.openSync(url, fs.OpenMode.READ_WRITE); // 打开文件let stat = fs.statSync(url); // 获取文件状态let buf = new ArrayBuffer(stat.size); // 创建一个ArrayBuffer对象let base64 = new util.Base64Helper(); // 实例化Base64Helperlet num = fs.readSync(file.fd, buf); // 读取文件let data = base64.encodeSync(new Uint8Array(buf.slice(0, num))) //  转换成Uint8Arraylet textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })let retStr = textDecoder.decodeWithStream(data, { stream: false }); // 可以把Uint8Array转码成base64let fileName = file.namefs.closeSync(file);return { base64: retStr, fileName: fileName } as IBase64;}// 下载pdf文件,获取沙箱文件目录getTempFile(fileUrl:string){let context = getContext(this) as common.UIAbilityContext;const fileFullName = fileUrl.split('/')[fileUrl.split('/').length - 1]let tempFilePath = `${context.filesDir}/${fileFullName}`;//文件如果已经存在,就删除if (fs.accessSync(tempFilePath)) {fs.unlink(tempFilePath)}request.downloadFile(getContext(), { url: fileUrl,filePath: tempFilePath }).then((data: request.DownloadTask) => {let downloadTask: request.DownloadTask = data;let progressCallback = (receivedSize: number, totalSize: number) => {// 这里可以自行编写下载进度条showToast(`下载大小${receivedSize},总大小${totalSize}`);};let completeCallback = ()=>{showToast("下载完毕");this.tempFilePath = tempFilePath;}downloadTask.on('progress', progressCallback);downloadTask.on('complete', completeCallback)}).catch((err: BusinessError) => {console.error(`Failed to request the download. Code: ${err.code}, message: ${err.message}`);})}// 组件生命周期:组件即将出现时回调该接口aboutToAppear() {console.log('进入页面')// 你的pdf链接this.fileUrl = (router.getParams() as Record<string, string>).url || '';this.getTempFile((router.getParams() as Record<string, string>).url as string);}
}

这里附有将pdf文件下载到沙箱目录代码,可选择使用(不必须)。

效果中的整体代码

import web_webview from '@ohos.web.webview';
import promptAction from '@ohos.promptAction'
import { BusinessError, request } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
import { util } from '@kit.ArkTS';// pdf页面tab接口
interface pageTab {name:string;
}interface IBase64 {base64: string;fileName: string;
}/*** pdfPage的ViewModel*/
class PdfPageModel {// 当前索引curTabIndex:number = 0;// pdf页面tabtabList:pageTab[] = [{ name:'预览本地PDF文件' },{ name:'预览网络PDF文件' },{ name:'预览沙箱PDF文件' },];// 网络文件fileUrl: string = 'http://www.cztouch.com/upfiles/soft/testpdf.pdf'// 本地沙箱文件地址tempFilePath: string = ''constructor() {}// 沙箱pdf文件转base64方法sandBoxPdfToBase64(url: string) {let file = fs.openSync(url, fs.OpenMode.READ_WRITE); // 打开文件let stat = fs.statSync(url); // 获取文件状态let buf = new ArrayBuffer(stat.size); // 创建一个ArrayBuffer对象let base64 = new util.Base64Helper(); // 实例化Base64Helperlet num = fs.readSync(file.fd, buf); // 读取文件let data = base64.encodeSync(new Uint8Array(buf.slice(0, num))) //  转换成Uint8Arraylet textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })let retStr = textDecoder.decodeWithStream(data, { stream: false }); // 可以把Uint8Array转码成base64let fileName = file.namefs.closeSync(file);return { base64: retStr, fileName: fileName } as IBase64;}// 下载pdf文件,获取沙箱文件目录getTempFile(fileUrl:string){let context = getContext(this) as common.UIAbilityContext;const fileFullName = fileUrl.split('/')[fileUrl.split('/').length - 1]let tempFilePath = `${context.filesDir}/${fileFullName}`;//文件如果已经存在,就删除if (fs.accessSync(tempFilePath)) {fs.unlink(tempFilePath)}request.downloadFile(getContext(), { url: fileUrl,filePath: tempFilePath }).then((data: request.DownloadTask) => {let downloadTask: request.DownloadTask = data;let progressCallback = (receivedSize: number, totalSize: number) => {// 这里可以自行编写下载进度条// showToast(`下载大小${receivedSize},总大小${totalSize}`);};let completeCallback = ()=>{// showToast("下载完毕");this.tempFilePath = tempFilePath;}downloadTask.on('progress', progressCallback);downloadTask.on('complete', completeCallback)}).catch((err: BusinessError) => {console.error(`Failed to request the download. Code: ${err.code}, message: ${err.message}`);})}// tab切换switchTab(index:number){this.curTabIndex = index;if(index === 2 && !this.tempFilePath){try {promptAction.showDialog({title: '温馨提示',message: '有些pdf线上链接是经过第三方加密过的,在浏览器访问时不能直接预览,直接走的是下载的pdf文件链接,可以采用这种方式,先下载在沙箱目录中,然后再预览沙箱中的pdf文件',buttons: [{text: '知道了',color: '#000000'}]}, (err, data) => {if (err) {console.error('showDialog err: ' + err);return;}console.info('showDialog success callback, click button: ' + data.index);});} catch (error) {console.error(`Failed to show dialog. Code: ${error.code}, message: ${error.message}`);}this.getTempFile(this.fileUrl);}}
}@Entry
@Component
struct PdfPage {webviewController: web_webview.WebviewController = new web_webview.WebviewController();@State vm: PdfPageModel = new PdfPageModel();// 验证是否选中VerifySelectedFun( curIndex:number , itemIndex:number ):boolean{return curIndex == itemIndex}aboutToAppear(): void {try {promptAction.showDialog({title: '温馨提示',message: '在模拟器中运行,首次加载会出现黑屏,但来回切换几次tab标签就好了,有条件的建议使用真机运行,不会有这样的问题',buttons: [{text: '知道了',color: '#000000'}]}, (err, data) => {if (err) {console.error('showDialog err: ' + err);return;}console.info('showDialog success callback, click button: ' + data.index);});} catch (error) {console.error(`Failed to show dialog. Code: ${error.code}, message: ${error.message}`);}}build() {Stack() {Column() {// tab标签条Row(){Scroll(){Row(){ForEach(this.vm.tabList,(item:pageTab,index)=>{Row(){if(this.VerifySelectedFun(this.vm.curTabIndex,index)){Stack(){Row(){}.width(40).height(10).borderRadius(20).offset({y:7}).linearGradient({angle:89.11,colors:[['rgba(255, 255, 255, 0.55)',0.0682],['rgba(217, 217, 217, 0)',1]]})Text(item.name).fontSize(18).fontColor($r('app.color.primary_theme_color')).fontWeight(600).height('100%')}}else{Text(item.name).fontSize(16)// .fontColor($r('app.color.font_color_default')).fontWeight(400)}}.height('100%').justifyContent(FlexAlign.Start).padding({ left: index == 0 ? 0 : 20 }).onClick(()=>{this.vm.switchTab(index)})})}}.edgeEffect(EdgeEffect.Fade).layoutWeight(1).align(Alignment.Center).scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Off)}.width('100%').height(50).justifyContent(FlexAlign.Start).padding({left:16,right:16}).backgroundColor(Color.White)// 页面内容Scroll(){Column(){if(this.vm.curTabIndex === 0 ){// web组件加载本地pdf文件Web({ src: $rawfile('Git.pdf'), controller: this.webviewController}).domStorageAccess(true).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)})}else if(this.vm.curTabIndex === 1){// web组件加载网络pdf文件Web({ src: 'http://www.cztouch.com/upfiles/soft/testpdf.pdf', controller: this.webviewController }).layoutWeight(1).domStorageAccess(true).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)})}else if(this.vm.curTabIndex === 2){if(this.vm.tempFilePath){Web({ src: $rawfile('pdfviewer/viewer.html'), controller: this.webviewController }).onProgressChange((event)=>{console.log("newProgress", event?.newProgress)}).domStorageAccess(true) // 设置是否开启文档对象模型存储接口(DOM Storage API)权限,默认未开启。.onPageEnd(()=>{let file = this.vm.sandBoxPdfToBase64(this.vm.tempFilePath);this.webviewController.runJavaScript(`openFile("${file.base64}", "${file.fileName}")`);})}}}.padding({ left: 16, right: 16, bottom: 16 })}.edgeEffect(EdgeEffect.Fade).width('100%').layoutWeight(1).align(Alignment.TopStart)}.height('100%').backgroundColor('#F5F5F5').padding({ bottom: 16 })}}
}

总结

总体来说就是使用Web组件加载pdf文件,在模拟器中运行,首次运行会黑屏,不过来回切换一下tab页就好了,真机运行没有问题。
为啥要存到沙箱中再预览,岂不是多此一举?
当然不是,因为有的pdf文件是通过第三方加密过的,在浏览器打开链接时,是不能直接预览的,而是直接走下载了。这时,就需要先存到沙箱目录中再预览。

有需要的朋友,拿走不谢,求赞,求赞,求赞~
关注我不迷路,不定时分享鸿蒙难点亮点

Harmony OS NEXT版本(接口及解决方案兼容API12版本或以上版本)

相关文章:

鸿蒙ArkTs如何实现pdf预览功能?

鸿蒙ArkTs如何实现pdf预览功能&#xff1f; 前言PDFKit运行示例代码报错真机运行先看效果一、预览本地pdf文件二、预览线上的pdf文件三、预览沙箱目录中pdf的文件(重点)效果中的整体代码总结 Harmony OS NEXT版本&#xff08;接口及解决方案兼容API12版本或以上版本) 前言 在开…...

KylinSP3 | 防火墙和麒麟安全增强设置KySec

一、系统防火墙原理 麒麟操作系统从V10版本开始&#xff0c;默认使用了Firewalld防火墙&#xff0c;Firewalld是能提供动态管理的防火墙&#xff0c;支持网络/防火墙区域&#xff0c;用于定义网络连接或接口的信任级别。支持IPv4和IPv6防火墙设置、以太网桥接和IP集。将运行时…...

【C++】面试常问八股

5、内存管理 野指针 野指针指的是未进行初始化或未清零的指针&#xff0c;不是NULL指针野指针产生原因及解决方案&#xff1a; 指针变量未初始化&#xff1a;指针变量定义时若未初始化&#xff0c;则其指向的地址是随机的&#xff0c;不为NULL&#xff1b;定义时初始化为NULL…...

vscode多文件编译构建(CMake)和调试C++

目录 1. CMake 基础构建工具及作用相关配置文件 2. 配置 tasks.json关键字段详细解释 3. 配置 launch.json关键字段详细解释 4. 配置 CMakeLists.txt关键部分详细解释 5. 构建和调试项目1. 仅构建项目1.1 任务执行顺序1.2 cmake 任务执行详情1.3 build 任务执行详情1.4 构建后的…...

使用Docker 部署 LNMP+Redis 环境

使用Docker 部署 LNMPRedis 环境 Docker 简介 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互…...

文件上传漏洞学习笔记

一、漏洞概述 定义 文件上传漏洞指未对用户上传的文件进行充分安全校验&#xff0c;导致攻击者可上传恶意文件&#xff08;如Webshell、木马&#xff09;&#xff0c;进而控制服务器或执行任意代码。 危害等级 ⚠️ 高危漏洞&#xff08;通常CVSS评分7.0&#xff09;&#xff…...

375_C++_cloud手机推送,添加人脸告警信息到任务队列中,UploadAlarmPush是典型的工厂模式应用,为什么使用工厂模式完成这部分代码

一:AlarmFaceInfo的应用 让我帮你解析这个lambda表达式的实现: // ...................... .h ...........................// struct RsMsgPushTask_S : public Task{AlarmType_E mainAlarmType;unsigned int subAlarmType;DateTime alarmTime...

Spring Boot 中的日志管理

一、日志框架选择 1. 主流框架对比 框架特点Spring Boot 默认支持Logback- 性能优异&#xff0c;Spring Boot 默认集成- 支持自动热更新配置文件✅ (默认)Log4j2- 异步日志性能更强- 支持插件扩展- 防范漏洞能力更好❌ (需手动配置)JUL (JDK自带)- 无需额外依赖- 功能简单&am…...

火绒终端安全管理系统V2.0网络防御功能介绍

火绒终端安全管理系统V2.0 【火绒企业版V2.0】网络防御功能包含网络入侵拦截、横向渗透防护、对外攻击检测、僵尸网络防护、Web服务保护、暴破攻击防护、远程登录防护、恶意网址拦截。火绒企业版V2.0的网络防御功能&#xff0c;多层次、多方位&#xff0c;守护用户终端安全。 …...

海康摄像头 + M7s(Monibuca) + FFmpeg + Python实现多个网络摄像头视频流推流

最近在研究流媒体服务器时&#xff0c;我注意到了一款开源软件——M7s。按照官网的指南部署完成后&#xff0c;我开始进行测试&#xff0c;发现单视频流推送非常顺利&#xff0c;没有任何问题。然而&#xff0c;当我尝试进行多视频流推送时&#xff0c;却发现网上的相关教程寥寥…...

抖音视频如何下载保存去水印

随着短视频平台的兴起&#xff0c;抖音作为国内最受欢迎的短视频平台之一&#xff0c;吸引了大量用户上传和观看各种创意视频。许多用户在浏览抖音视频时&#xff0c;往往会想要保存一些有趣或精彩的视频片段&#xff0c;但抖音视频通常会有水印&#xff0c;影响观看体验。为了…...

【鸿蒙开发】第三十九章 LazyForEach:数据懒加载

目录 1 背景 2 使用限制 键值生成规则 组件创建规则 首次渲染 非首次渲染 改变数据子属性 使用状态管理V2 拖拽排序 1 背景 LazyForEach从提供的数据源中按需迭代数据&#xff0c;并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach&#xff0c;框架…...

HTTP-

一.HTTP 1.什么是HTTP HTTP(超文本传输协议)是一种工作在应用层的协议.主要用于网站,就是浏览器和服务器之间的数据传输. 小知识:什么是超文本传输协议 文本:是字符串.(能在utf8/gbk码表上找到合法字符) 超文本:不仅可以传输字符串,也可以传输图片,html等 富文本:word文档 2.HT…...

创建型模式 - 原型模式 (Prototype Pattern)

创建型模式 -原型模式 (Prototype Pattern) 它允许通过复制现有对象来创建新对象&#xff0c;而无需知道对象的具体创建细节。在 Java 中&#xff0c;可以通过实现 Cloneable 接口和重写 clone() 方法来实现原型模式。 有深、浅两种克隆 类实现 Cloneable 接口就可以深克隆如果…...

Android 8.0 (API 26) 对广播机制做了哪些变化

大部分隐式广播无法通过静态注册接收&#xff0c;除了以下白名单广播&#xff1a; ACTION_BOOT_COMPLETED ACTION_TIMEZONE_CHANGED ACTION_LOCALE_CHANGED ACTION_MY_PACKAGE_REPLACED ACTION_PACKAGE_ADDED ACTION_PACKAGE_REMOVED 需要以动态注册方案替换&#xff1a; cl…...

Unity汽车笔记

汽车的移动和转向 我们知道&#xff0c;汽车的前进后退是变速运动。按w&#xff0c;汽车开始加速&#xff0c;到最大速度后保持匀速&#xff0c;松开w&#xff0c;汽车受到阻力加速。如果按s减速&#xff0c;则以更大的加速度减速。后退反之。 按A/D时前轮偏转。只有前进后退…...

html中rel、href、src、url的区别

1.url url&#xff08;统一资源定位符&#xff09;&#xff1a;是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示&#xff0c;是互联网上标准资源的地址。 2.href href&#xff1a;Hypertext Reference的缩写。 意思是超文本引用。 3.rel rel&#xff1a;relatio…...

【idea问题排查技巧】

以下是针对 IDEA 中 日志打标(动态标记) 和 全链路追踪 功能的分步详解,结合具体场景和操作截图说明,帮助快速掌握实战技巧。 一、动态日志打标:不修改代码输出关键信息 1. 断点日志打印(非侵入式打标) 场景:在调试时,需要临时查看某个变量的值,但不想修改代码添加…...

SQL: DDL,DML,DCL,DTL,TCL,

Structured Query Language&#xff0c;结构化查询语言, 是一种用于管理和操作关系数据库的标准编程语言。 sql的分类 DQL&#xff08;Data Query Language&#xff09;&#xff1a;数据查询语言 DDL&#xff08;Data Definition Language&#xff09;&#xff1a;数据定义语…...

WordPress R+L Carrier Edition sql注入漏洞复现(CVE-2024-13481)(附脚本)

免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 0x0…...

Coze扣子音频组件与数据库管理功能全面解析

1. Coze扣子音频组件实战指南 最近Coze扣子平台迎来了一次重要更新&#xff0c;其中最让我兴奋的就是音频组件的加入。作为一个经常需要在小程序中集成多媒体功能的开发者&#xff0c;这次更新直接解决了我们项目中的一大痛点。还记得上个月为了在小程序里实现音频播放&#xf…...

2026届毕业生推荐的五大降AI率神器推荐榜单

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 降低 AIGC&#xff08;人工智能生成内容&#xff09;检测率的有效途径包含这些&#xff1a;…...

从HTB CozyHosting靶机渗透实战看SpringBoot应用安全与权限提升

1. 靶机环境初探与信息收集 第一次接触HTB的CozyHosting靶机时&#xff0c;我习惯性地从基础信息收集开始。用nmap快速扫描目标IP&#xff08;10.10.11.230&#xff09;&#xff0c;发现开放了四个关键端口&#xff1a;22(SSH)、80(HTTP)、8000(HTTP)、8081(未知服务)。这里有个…...

F2803x DSP ePWM模块实战:从基础配置到高精度电机控制

1. ePWM模块基础配置与电机控制入门 第一次接触F2803x的ePWM模块时&#xff0c;我完全被那些专业术语搞懵了。什么时基模块、比较模块、动作模块&#xff0c;听起来就像天书。但当我真正动手配置一个简单的电机驱动电路后&#xff0c;才发现这套系统设计得非常巧妙。下面我就用…...

实战复盘:我是如何用R包SpiecEasi里的Sparcc,从土壤微生物数据中挖出关键互作关系的

从土壤微生物数据到共现网络&#xff1a;我的Sparcc实战经验分享 去年在研究连作障碍土壤微生物群落变化时&#xff0c;我遇到了一个棘手的问题&#xff1a;如何从海量的OTU数据中找出那些真正有生态学意义的微生物互作关系&#xff1f;经过反复尝试&#xff0c;最终通过SpiecE…...

避开二轴机械臂动力学建模的坑:摩擦、噪声与激励轨迹设计实战

二轴机械臂动力学建模实战&#xff1a;从摩擦处理到激励轨迹设计的工程精要 在工业自动化与协作机器人快速发展的今天&#xff0c;精确的动力学建模已成为实现高精度控制的基础。不同于教科书中的理想化推导&#xff0c;真实机械臂建模过程中工程师们常会遇到三大"拦路虎&…...

国民技术 N32G031K8L7 LQFP-32 单片机

内核CPU 32位ARM Cortex-M0 内核&#xff0c;单周期硬件乘法指令 最高主频48MHz 加密存储器 - 高达64KByte片内Flash&#xff0c;支持加密存储&#xff0c;支持硬件ECC校验&#xff0c;10万次擦写次数&#xff0c;10年数据保持 一8KByte片内SRAM&#xff0c;支持硬件奇偶校验 低…...

5分钟上手LogcatReader:安卓设备日志查看神器

5分钟上手LogcatReader&#xff1a;安卓设备日志查看神器 【免费下载链接】LogcatReader A simple app for viewing logcat logs on an android device. 项目地址: https://gitcode.com/gh_mirrors/lo/LogcatReader 还在为复杂的ADB命令而烦恼吗&#xff1f;LogcatReade…...

别再为数据采集发愁了!用这个桥接器,5分钟搞定三菱FX3U PLC的ModbusTCP通讯

工业现场通讯革命&#xff1a;三菱FX3U PLC的ModbusTCP极简配置指南 在自动化产线的调试现场&#xff0c;时间就是成本。当项目总监要求你在两小时内将老旧的FX3U PLC数据接入MES系统时&#xff0c;传统方案需要重写PLC程序、配置专用模块&#xff0c;甚至可能面临硬件改造——…...

SAP预留与锁料功能深度对比:如何选择最适合你的物料控制方案

SAP预留与锁料功能深度对比&#xff1a;如何选择最适合你的物料控制方案 物料管理是制造业企业运营的核心环节之一。在SAP系统中&#xff0c;预留(Reservation)和锁料(Material Blocking)是两种常用的物料控制机制&#xff0c;它们都能确保关键物料在需要时可用&#xff0c;但实…...