文件传输 (OpcUaFile)
概述
OPC UA Part 5 §10 定义 FileType (i=11575) 标准对象, 暴露 6 个 Method:
Open / Read / Write / Close / GetPosition / SetPosition。
OpcUaFile 把这 6 个 Method 封装为面向流式调用的 .NET API, 适用场景:
- 固件升级 (PLC / IO / Drive 镜像下载)
- 配方 (Recipe) 上传 / 下载
- 任意文件镜像同步
对应规范段: Part 5 §10 (FileType)。
API
| 方法 / 属性 | 类别 | 读写 | 说明 |
|---|---|---|---|
| new OpcUaFile(ua, fileNodeId) | 构造 | — | 绑定到服务端某个 FileType 实例 |
| file.Size | 属性 | 读 | 文件大小 (UInt64) |
| file.Writable | 属性 | 读 | 是否可写 (Boolean) |
| file.UserWritable | 属性 | 读 | 当前用户是否可写 (Boolean) |
| file.OpenCount | 属性 | 读 | 当前打开句柄数 (UInt16) |
| file.Open(mode) | 方法 | 写 | 打开句柄, 返回 fileHandle |
| file.Read(length) | 方法 | 读 | 读取 N 字节 |
| file.Write(bytes) | 方法 | 写 | 写入字节 |
| file.GetPosition() | 方法 | 读 | 当前文件指针 |
| file.SetPosition(pos) | 方法 | 写 | 设置文件指针 |
| file.Close() | 方法 | 写 | 关闭句柄 (Dispose 自动调) |
| file.DownloadTo(localPath, progress) | 高层 | 读 | 一键下载到本地文件 |
| file.UploadFrom(localPath, progress) | 高层 | 写 | 一键从本地文件上传 |
相关结构:
[Flags]
public enum FileOpenMode : byte
{
Read = 0x01, // 读
Write = 0x02, // 写
EraseExisting = 0x04, // 清空 (与 Write 组合)
Append = 0x08 // 追加 (打开后自动 SeekToEnd)
}
代码示例
using DarraOpcUa_Client;
using var ua = new DarraOpcUa("opc.tcp://device:4840");
ua.Connect();
// 1) 一键下载固件
using (var file = new OpcUaFile(ua, "ns=2;s=Firmware.Image"))
{
Console.WriteLine($"Size = {file.Size} bytes");
var rc = file.DownloadTo(@"C:\dl\firmware.bin",
new Progress<(ulong d, ulong t)>(p =>
Console.Write($"\r{p.d * 100 / p.t}% ({p.d}/{p.t})")));
if (rc != StatusCode.Good) Console.WriteLine($"\n下载失败: {rc}");
}
// 2) 一键上传配方 (清空后写)
using (var f2 = new OpcUaFile(ua, "ns=2;s=Recipe.Current"))
{
var rc = f2.UploadFrom(@"C:\recipes\new.csv");
if (rc != StatusCode.Good) Console.WriteLine($"上传失败: {rc}");
}
// 3) 手动流式读 (大文件分块自定义处理)
using (var f3 = new OpcUaFile(ua, "ns=2;s=Logs.Current"))
{
f3.Open(FileOpenMode.Read);
while (true)
{
var (st, chunk) = f3.Read(4096);
if (st != StatusCode.Good || chunk == null || chunk.Length == 0) break;
ProcessChunk(chunk);
}
f3.Close();
}
错误处理
- 业务级失败 → 返回
StatusCode(BadInvalidArgument/BadUserAccessDenied/BadNotWritable/ ...) - transport 失败 → throw
OpcUaException using退出 / 异常路径 → 自动Close已打开的句柄 (实现IDisposable)
实现说明
- 块大小默认 4 KB (大多数 Server 限 8 KB 以内, 留余量)
- ByteString 走 ISO-8859-1 字符串桥接, 无损保留 0x00-0xFF 全字节
- Method NodeId 解析: 优先 TranslateBrowsePaths 找实例 Method, 失败 fallback 类级 NodeId (
i=11580等)
最佳实践
- 始终用
using— 漏 Close 会让服务端句柄表泄漏 - 进度回调切 UI 线程 —
Progress<T>默认捕获当前 SynchronizationContext, WPF/WinForms 调用方在 UI 线程构造即可 - 大文件考虑分段重试 — 上层在每个 4KB 块边界做 checkpoint, 网络断后续传
- 不要并发同一 fileHandle — 一个 Open 对应一个串行流
跨语言对照
| C# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| OpcUaFile | OpcUaFile | OpcUaFile | OpcUaFile | OpcUaFile | DarraUa_File_* |
| DownloadTo | download_to | downloadTo | DownloadTo | download_to | DarraUa_File_DownloadTo |
| UploadFrom | upload_from | uploadFrom | UploadFrom | upload_from | DarraUa_File_UploadFrom |