ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Windows-universal-samples 中的 DataReader/DataWriter 数据序列化与反序列化实战解析

Windows-universal-samples 中的 DataReader/DataWriter 数据序列化与反序列化实战解析 示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载本文以 Windows-universal-samples 仓库归档示例archived/DataReaderWriter为核心系统讲解 UWPUniversal Windows Platform中Windows.Storage.Streams.DataReader与DataWriter两大核心类的工作原理、完整调用链与实战用法。你将掌握两种典型场景一是在内存流中写入并回读结构化字符串数据二是通过顺序访问流结合readBytes()对二进制文件如图片进行分块读取并输出十六进制转储hex dump并理解storeAsync、detachStream、loadAsync、unconsumedBufferLength等关键 API 的生命周期管理细节。示例概览它在仓库中的位置与价值该示例位于仓库的 archived/DataReaderWriter 目录下属于 UWP 功能示例大集合中的一份归档样本。示例的 JavaScript 实现版本包含以下核心文件js/js/scenario1_WriteReadStream.js场景 1 的实现负责内存流写入与回读字符串。js/js/scenario2_ReadBytes.js场景 2 的实现负责对图片文件进行分块读取并生成十六进制转储。js/html/scenario1_WriteReadStream.html 与 js/html/scenario2_ReadBytes.html两个场景的界面载体。js/js/sample-configuration.js注册两个场景并定义示例标题的配置脚本。js/DataReaderWriter.jsproj 与 js/Package.appxmanifest项目文件与应用清单声明了 UAPUniversal Application Platform目标平台版本。从 js/DataReaderWriter.jsproj 可以看到该项目以TargetPlatformIdentifier为 UAP、TargetPlatformVersion为 10.0.18362.0而从 js/Package.appxmanifest 中可以确认其TargetDeviceFamily为Windows.Universal最小支持版本 10.0.10240.0即 Windows 10 桌面、移动端均在其适用范围之内。示例共演示两个场景见 js/js/sample-configuration.jsRead and write simple structured data使用DataWriter向内存流写入带长度前缀的字符串再用DataReader按长度回读。Dump file contents using readBytes()打开图片的顺序访问流用readBytes()分块读取并格式化输出十六进制转储。核心概念DataReader / DataWriter 与 Windows Runtime 流DataReader与DataWriter位于Windows.Storage.Streams命名空间是 UWP 中面向字节流的读写适配层。它们本身不直接持有存储介质而是依附于各类IRandomAccessStream或IInputStream实现例如InMemoryRandomAccessStream内存后备流读写数据不落盘适合传输、序列化与单元测试场景。StorageFile的随机访问流OpenAsync获取与顺序访问流OpenSequentialReadAsync获取面向文件内容。网络流如StreamSocket的输入/输出流面向网络传输。从源码实现看场景 1 的关键点在于写端与读端必须约定一致的编码与字节序。示例中写端设置writer.unicodeEncoding UnicodeEncoding.utf8、writer.byteOrder ByteOrder.littleEndian见 scenario1_WriteReadStream.js读端必须使用完全相同的设置见 scenario1_WriteReadStream.js否则回读内容将出现乱码或字节序颠倒。这在任何跨进程、跨设备的数据交换中都是决定性的约定。场景 1 深入内存流中的字符串写入与回读写端流程measureString 与长度前缀协议场景 1 的界面scenario1_WriteReadStream.html包含一个待发送文本区ElementsToSend与一个 Copy strings 按钮。点击后触发transferData()其完整写入逻辑如下scenario1_WriteReadStream.jsvar writer Windows.Storage.Streams.DataWriter( new Windows.Storage.Streams.InMemoryRandomAccessStream()); writer.unicodeEncoding Windows.Storage.Streams.UnicodeEncoding.utf8; writer.byteOrder Windows.Storage.Streams.ByteOrder.littleEndian; var elements sourceElement.innerHTML.split(;); elements.forEach(function (element) { var codeUnits writer.measureString(element); writer.writeInt32(codeUnits); // 先写长度int324 字节 writer.writeString(element); // 再写字符串本体 });这里有一个容易被忽略但至关重要的协议设计writeString(element)写入的是指定编码下的字符串字节而回读端readString(codeUnitsToRead)需要的是一个码元code units长度参数见 scenario1_WriteReadStream.js 的注释。因此写端必须先调用writer.measureString(element)测量该字符串的码元长度并以 32 位整数writeInt32写入流中作为长度前缀随后才写入字符串本体。这种长度前缀 载荷的组帧方式是二进制流上可靠传输变长数据的经典做法。示例测试数据为Hello;World;1 2 3 4 5;Très bien!;Goodbye见 scenario1_WriteReadStream.js其中包含非 ASCII 字符é用于验证 UTF-8 编码下码元长度与字节长度的差异。提交与流生命周期storeAsync → flushAsync → detachStream → close写入完成后数据并不会自动进入后备流必须显式提交。示例中的完整提交链scenario1_WriteReadStream.js如下writer.storeAsync().then(function () { // 内存流实现下 flushAsync 是冗余的但文件流/网络流可能必须调用 return writer.flushAsync(); }).then(function () { // 分离流延长其生命周期 stream writer.detachStream(); stream.seek(0); writer.close(); reader new Windows.Storage.Streams.DataReader(stream); reader.unicodeEncoding Windows.Storage.Streams.UnicodeEncoding.utf8; reader.byteOrder Windows.Storage.Streams.ByteOrder.littleEndian; return reader.loadAsync(stream.size); });这段代码蕴含了三条重要的资源管理准则storeAsync 与 flushAsync 的区别storeAsync()将DataWriter内部缓冲区的数据写入后备流flushAsync()确保底层流完成物理落盘/发送。对InMemoryRandomAccessStream而言flushAsync是冗余的源码注释明确说明这一点但对文件流或网络流则可能是必需的。detachStream 的用途调用writer.close()会连带关闭底层流导致后续DataReader无法再使用它。detachStream()将流从 writer 上解绑使流能继续被DataReader读取作为代价分离后流的关闭责任转移给调用方此时writer.close()不再对流产生任何影响。源码注释同时强调大多数不分离流的DataWriter客户端都应该调用writer.close()这是最佳实践见 scenario1_WriteReadStream.js。seek(0) 的必要性内存流在写入后其当前位置指针已位于流尾回读前必须stream.seek(0)将指针复位到流起点见 scenario1_WriteReadStream.js。读端流程loadAsync 与 unconsumedBufferLength读端通过reader.loadAsync(stream.size)一次性将全部内容加载进DataReader内部缓冲区这也是异步操作随后循环消费while (reader.unconsumedBufferLength 0) { var codeUnitsToRead reader.readInt32(); receivedStrings reader.readString(codeUnitsToRead) br/; } reader.close(); destinationElement.innerHTML receivedStrings;循环条件是reader.unconsumedBufferLength——这是DataReader尚未消费的缓冲字节数随每次读取递减当它归零时表明流已完全消费完毕。每次迭代先readInt32()取出长度前缀再readString(codeUnitsToRead)按码元长度读取字符串完美对称于写端的writeInt32writeString。最后reader.close()关闭 reader——由于流此前已被分离这次调用同时履行了分离后对流对象的关闭义务见 scenario1_WriteReadStream.js。场景 2 深入顺序访问流上的二进制分块读取与十六进制转储打开顺序流OpenSequentialReadAsync场景 2 的目标是对一张 PNG 图片ms-appx:///images/microsoft-sdk.png实际链接自仓库的 SharedContent/media/microsoft-sdk.png参见 DataReaderWriter.jsproj进行十六进制转储。打开流程scenario2_ReadBytes.jsvar uri new Windows.Foundation.Uri(ms-appx:///images/microsoft-sdk.png); Windows.Storage.StorageFile.getFileFromApplicationUriAsync(uri).then(function (file) { return file.openSequentialReadAsync(); }).done(function (inputStream) { dataReader new Windows.Storage.Streams.DataReader(inputStream); readLoop(dataReader); });openSequentialReadAsync()返回IInputStream类型的顺序访问流——它只能从前向后读取不支持随机定位但开销低于随机访问流非常适合从头到尾全量消费的场景。DataReader直接以该输入流为数据源进行读取。界面层scenario2_ReadBytes.html展示该图片并提供 Hex Dump 按钮输出区readBytesOutput在样式上被设置为等宽字体见 css/scenario2_ReadBytes.css以保证十六进制对齐效果。分块读取loadAsync 与 readBytes 的组合二进制数据往往较大不宜一次性载入内存因此示例采用固定块大小 逐行消费的策略关键常量scenario2_ReadBytes.jsvar bytesPerRow 16; // 每行 16 字节 var bytesPerSegment 2; // 每 2 字节一个分段用于格式化 var chunkSize 4096; // 每次 loadAsync 加载 4096 字节核心的readLoop递归实现scenario2_ReadBytes.jsdataReader.loadAsync(chunkSize).done(function (numBytes) { var bytes new Uint8Array(bytesPerRow); var numBytesRemaining numBytes; while (numBytesRemaining bytesPerRow) { dataReader.readBytes(bytes); // 填满一整行 outputStr formatRow(bytes, (numBytes - numBytesRemaining) (currChunk * chunkSize)); numBytesRemaining - bytesPerRow; } if (numBytesRemaining 0) { bytes new Uint8Array(numBytesRemaining); // 剩余不足一行的字节 dataReader.readBytes(bytes); outputStr formatRow(bytes, (numBytes - numBytesRemaining) (currChunk * chunkSize)); } currChunk; if (numBytes chunkSize) { document.getElementById(readBytesOutput).innerHTML outputStr; dataReader.close(); // 全部读完确定性释放资源 } else { // 用 timeout(0) 启动下一轮避免 loadAsync 同步完成导致的深层递归/栈溢出 WinJS.Promise.timeout(0).done(function () { return readLoop(dataReader); }); } });这里有三个值得注意的实现细节readBytes 的数组长度语义readBytes(bytes)会尝试填满整个数组若传入数组大于 DataReader 缓冲区剩余字节数将抛出异常。因此当剩余不足一整行时必须重新分配恰好容纳剩余字节的Uint8Array见 scenario2_ReadBytes.js 的注释与代码。终止条件loadAsync返回的numBytes即本次实际加载的字节数当它小于chunkSize时说明文件已被全部读完此时输出结果并关闭dataReader以确定性释放资源见 scenario2_ReadBytes.js。防栈溢出设计由于loadAsync被允许同步完成若直接递归调用readLoop在大量同步完成的场景下可能造成深层递归乃至栈溢出示例改用WinJS.Promise.timeout(0)将下一轮迭代推迟到下一个事件循环见 scenario2_ReadBytes.js。行格式化地址与字节的十六进制输出formatRowscenario2_ReadBytes.js负责将每一行字节转为可读的十六进制文本rowStr (0000000 currByte.toString(16)).substr(-8); // 8 位十六进制地址 for (var i 0; i bytes.length; i) { if (i % bytesPerSegment 0) { rowStr ; // 每 2 字节插入一个空格分段 } rowStr (0 bytes[i].toString(16)).substr(-2); // 每个字节补零为 2 位十六进制 } return rowStr br /;通过substr(-8)与substr(-2)实现左侧补零保证每行地址固定 8 位、每字节固定 2 位最终输出形如00000000 89504e47 0d0a1a0a ...的标准十六进制转储与文件的实际二进制布局一一对应。这验证了readBytes()返回的字节序列与文件在磁盘上的真实内容完全一致是校验文件完整性、分析二进制格式的常用手段。构建与运行环境要求依据示例文档与项目配置系统要求客户端 Windows 10、服务器端 Windows Server 2016 Technical Preview、手机端 Windows 10应用清单中的TargetDeviceFamily MinVersion为 10.0.10240.0。构建工具示例文档声明需要 Visual Studio 2017 才能构建项目文件DataReaderWriter.jsproj为 JavaScript 版 UWP 项目最低VisualStudioVersion14.0。构建步骤若通过 ZIP 下载整个样本集合请务必解压整个压缩包而不仅是单个示例文件夹——示例依赖仓库根目录下的共享内容如 WinJS 库与共享媒体资源从 DataReaderWriter.jsproj 中可见其通过..\..\..\SharedContent\...相对路径引用共享文件。启动 Visual Studio选择File Open Project/Solution。进入解压目录依次进入 Samples 子目录、本示例子目录、首选语言子目录本示例为 JavaScript双击打开DataReaderWriter.sln解决方案文件位于 archived/DataReaderWriter/js/DataReaderWriter.sln。按CtrlShiftB或选择Build Build Solution完成编译。部署与运行仅部署选择Build Deploy Solution。部署并运行调试按F5或选择Debug Start Debugging。部署并运行不调试按CtrlF5或选择Debug Start Without Debugging。运行后通过示例自带的场景选择器由 js/js/sample-configuration.js 注册的两个场景列表驱动即可在写入回读与十六进制转储两个演示间切换。小结从示例提炼的可复用模式纵观整个示例可以提炼出三条在任何 UWP 流式数据处理中都可复用的模式成对约定写端与读端必须显式并一致地设置unicodeEncoding与byteOrder对变长字符串使用measureString测长 writeInt32前缀 writeString载荷的组帧协议。流生命周期责任链storeAsync/flushAsync负责数据落地detachStream会转移流的关闭责任DataReader.close()会连带关闭底层流。谁最后持有流谁负责关闭。大文件分块消费以固定chunkSize循环loadAsync以numBytes chunkSize作为读尽判据配合精确长度的readBytes数组与异步调度即可在不耗尽内存的前提下完成任意大小二进制文件的完整消费与格式化输出。对于希望进一步研究的读者完整源码可对照阅读 scenario1_WriteReadStream.js 与 scenario2_ReadBytes.js以及仓库中同主题的其他语言实现如 Samples/DataReaderWriter 下的 C 与 C# 版本从而对比不同语言对同一套 Windows Runtime 流 API 的调用方式。赞分享示例工程【免费下载链接】Windows-universal-samplesAPI samples for the Universal Windows Platform.项目地址https://gitcode.com/gh_mirrors/wi/Windows-universal-samples点击查看免费下载相关推荐Windows-universal-samples 中的 DataReader/DataWriter 数据序列化与反序列化实战指南Windows universal samples 中的 DataReader/DataWriter 数据序列化与反序列化实战指南 导读 本文围绕 Sample示例工程Android-DataBackup序列化数据序列化与反序列化Android DataBackup序列化数据序列化与反序列化 在Android数据备份领域高效可靠的数据序列化Serialization与反序列化D移动开发SponsorKit高级技巧多配置渲染与自动化生成多种格式赞助者图片SponsorKit高级技巧多配置渲染与自动化生成多种格式赞助者图片 SponsorKit是一款功能强大的赞助者图片生成工具能够帮助开源项目轻松创建专业的赞创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表