Writing files to an OPC UA server
This tutorial explains how to use the OPC UA File Operation node to write data to files stored on an OPC UA server.
Prerequisites
- An OPC UA server that exposes File objects (TypeDefinition = FileType, ns=0;i=11575)
- A configured OPC UA connection endpoint
- Write permissions on the target file
- Knowledge of the file's NodeId or browse path
Write Modes
The OPC UA File Operation node supports two write modes:
- Write (WriteEraseExisting): Completely replaces the file content with new data
- WriteAppend: Adds new data to the end of the existing file
Basic File Writing
Step 1: Configure the Node
- Drag an OPC UA File Operation node onto your flow
- Configure the following properties:
- Endpoint: Select your OPC UA server connection
- NodeId: Enter the NodeId of the file object (e.g.,
ns=1;s=MyFile) - Mode: Select Write
- Encoding: Select utf8 (for text files)
Step 2: Create a Simple Flow
[ Inject ] → [ Function ] → [ OPC UA File Operation ] → [ Debug ]
Step 3: Prepare Data and Write
Function Node:
msg.payload = "Hello World\nThis is my first file!";
return msg;
Click the inject node button. The file will be created/overwritten on the server.
Output:
msg.size: 35 // Number of bytes written
Writing Different Data Types
Writing Plain Text
Function Node:
msg.payload = "Simple text content";
return msg;
File Operation Configuration:
- Mode: Write
- Encoding: utf8
Writing JSON Data
The node automatically converts objects to JSON strings:
Function Node:
msg.payload = {
temperature: 25.5,
humidity: 60,
timestamp: new Date().toISOString(),
status: "normal"
};
return msg;
File Operation Configuration:
- Mode: Write
- Encoding: utf8
Resulting File Content:
{"temperature":25.5,"humidity":60,"timestamp":"2025-11-24T12:00:00.000Z","status":"normal"}
Writing CSV Data
Function Node:
const headers = "Name,Age,City";
const rows = [
"John,25,Paris",
"Jane,30,London",
"Bob,35,Berlin"
];
msg.payload = [headers, ...rows].join('\n');
return msg;
File Operation Configuration:
- Mode: Write
- Encoding: utf8
Resulting File:
Name,Age,City
John,25,Paris
Jane,30,London
Bob,35,Berlin
Writing Binary Data (Buffer)
For images, PDFs, or other binary files:
Function Node:
// Example: Write a small binary file
msg.payload = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
return msg;
File Operation Configuration:
- Mode: Write
- Encoding: none
Writing Numbers and Booleans
Automatically converted to strings:
Function Node:
msg.payload = 42;
// or
msg.payload = true;
return msg;
Resulting File: 42 or true
Overwriting vs Appending
Overwriting Files (Write Mode)
Completely replaces file content:
[ Timer (every hour) ] → [ Generate Report ] → [ Write File ] → [ Notify ]
Generate Report (Function):
const report = {
timestamp: new Date(),
metrics: {
processed: 1542,
errors: 3,
avgTime: 125
}
};
msg.payload = JSON.stringify(report, null, 2);
return msg;
Write File (OPC UA File Operation):
- Mode: Write
- NodeId:
/ns1:Reports/ns1:hourly.json - Encoding: utf8
Each hour, the file is completely replaced with the new report.
Appending to Files (WriteAppend Mode)
Adds content to the end of existing file:
[ Event Trigger ] → [ Format Log Entry ] → [ Append to Log ] → [ Debug ]
Format Log Entry (Function):
const timestamp = new Date().toISOString();
const level = msg.level || "INFO";
const message = msg.payload;
msg.payload = `${timestamp} - ${level} - ${message}\n`;
return msg;
Append to Log (OPC UA File Operation):
- Mode: WriteAppend
- NodeId:
ns=1;s=SystemLog - Encoding: utf8
Resulting File (after multiple appends):
2025-11-24T09:00:00.000Z - INFO - System started
2025-11-24T09:15:23.000Z - WARN - High memory usage detected
2025-11-24T09:30:45.000Z - ERROR - Connection failed
2025-11-24T10:00:00.000Z - INFO - Backup completed
Dynamic File Selection
Write to different files based on runtime conditions:
Flow:
[ Data Input ] → [ Route by Type ] → [ Write File ] → [ Confirm ]
Route by Type (Function):
const type = msg.payload.type;
const date = new Date().toISOString().split('T')[0];
// Select file based on data type
if (type === 'error') {
msg.nodeId = `/ns1:Logs/ns1:errors-${date}.log`;
} else if (type === 'audit') {
msg.nodeId = `/ns1:Logs/ns1:audit-${date}.log`;
} else {
msg.nodeId = `/ns1:Logs/ns1:general-${date}.log`;
}
// Format the log entry
msg.payload = `${new Date().toISOString()} - ${msg.payload.message}\n`;
return msg;
Write File (OPC UA File Operation):
- Mode: WriteAppend
- NodeId: (leave empty - set by msg.nodeId)
- Encoding: utf8
Working with Different Encodings
Writing UTF-8 Files (Default)
Configuration:
- Encoding: utf8
Suitable for: English, European languages, most modern text files
msg.payload = "Hello World! Héllo Wörld! 你好世界!";
Writing Shift_JIS Files (Japanese)
Configuration:
- Encoding: Shift_JIS
msg.payload = "こんにちは世界"; // Hello World in Japanese
Writing GB2312 Files (Chinese)
Configuration:
- Encoding: GB2312
msg.payload = "你好世界"; // Hello World in Chinese
Dynamic Encoding Based on Content
Configuration:
- Encoding: setbymsg
Function Node:
// Detect and set encoding based on content
const text = msg.payload;
if (/[\u3040-\u309F\u30A0-\u30FF]/.test(text)) {
// Contains Japanese characters
msg.encoding = 'Shift_JIS';
} else if (/[\u4E00-\u9FFF]/.test(text)) {
// Contains Chinese characters
msg.encoding = 'GB2312';
} else {
// Default to UTF-8
msg.encoding = 'utf8';
}
return msg;
Advanced Writing Scenarios
Writing Large Files
The node automatically handles large files by:
- Splitting data into optimized chunks
- Respecting server's MaxByteStringLength settings
- Writing chunks sequentially
Example - Writing Large JSON Export:
// Function node
const largeDataset = [];
for (let i = 0; i < 10000; i++) {
largeDataset.push({
id: i,
value: Math.random() * 100,
timestamp: new Date(Date.now() + i * 1000).toISOString()
});
}
msg.payload = JSON.stringify(largeDataset, null, 2);
return msg;
The node handles the large payload automatically - no special configuration needed!
Writing Configuration Files
Flow:
[ Config UI ] → [ Validate Config ] → [ Write Config ] → [ Apply Settings ]
Validate Config (Function):
// Validate configuration before writing
const config = msg.payload;
if (!config.server || !config.port) {
node.error("Invalid configuration: missing required fields");
return null;
}
// Add metadata
config.lastUpdated = new Date().toISOString();
config.version = "1.0";
msg.payload = JSON.stringify(config, null, 2);
return msg;
Write Config (OPC UA File Operation):
- Mode: Write (replace entire config)
- NodeId:
/ns1:Config/ns1:app-config.json - Encoding: utf8
Conditional Write (Create or Update)
[ Data ] → [ Check File ] → [ Switch ] → [ Write New ]
↓ ↓
[ Debug ] [ Append Existing ]
Check File (OPC UA File Operation #1):
- Mode: ReadSize
Switch Node:
- If msg.payload > 0 (file exists) → route to Append Existing
- If msg.error (file doesn't exist) → route to Write New
Write New (OPC UA File Operation #2):
- Mode: Write
Append Existing (OPC UA File Operation #3):
- Mode: WriteAppend
Backup Before Overwriting
[ New Data ] → [ Read Current ] → [ Save Backup ] → [ Write New ] → [ Success ]
Read Current (OPC UA File Operation #1):
- Mode: Read
- NodeId:
ns=1;s=DataFile
Save Backup (Function):
// Store current content for backup
flow.set('backup', msg.payload);
// Restore original message for write
msg.payload = flow.get('newData');
return msg;
Write New (OPC UA File Operation #2):
- Mode: Write
- NodeId:
ns=1;s=DataFile
Error Handling
Handling Write Errors
// In a Catch node watching the File Operation node
if (msg.error) {
node.warn("File write failed: " + msg.error);
// Take corrective action based on error type
if (msg.error.includes("BadNotWritable")) {
msg.payload = "File is not writable (check permissions)";
} else if (msg.error.includes("BadUserAccessDenied")) {
msg.payload = "Access denied - insufficient permissions";
} else if (msg.error.includes("BadNodeIdUnknown")) {
msg.payload = "File not found on server";
} else {
msg.payload = "Unknown error writing file";
}
// Log to alternative location
flow.set('failedWrites', flow.get('failedWrites') || []);
flow.get('failedWrites').push({
timestamp: new Date(),
data: msg.originalPayload,
error: msg.error
});
return msg;
}
Validating Write Success
// In a node after File Operation
if (msg.size) {
node.log(`Successfully wrote ${msg.size} bytes to file`);
msg.payload = {
success: true,
bytesWritten: msg.size,
timestamp: new Date()
};
} else {
node.warn("Write completed but size unknown");
}
return msg;
Monitoring Node Status
The node status indicator shows:
- "Operating": Writing in progress
- "size = X": Write completed successfully (X bytes written)
- "failed": Write operation failed (check debug panel for details)
Complete Example: Data Logger
Here's a complete example that logs sensor data with rotation:
[ MQTT In ] → [ Parse Data ] → [ Format Entry ] → [ Daily Router ] → [ Write Log ] → [ Notify ]
↓
[ Archive Old ]
Parse Data (Function):
msg.sensorData = JSON.parse(msg.payload);
msg.timestamp = new Date();
return msg;
Format Entry (Function):
const entry = {
timestamp: msg.timestamp.toISOString(),
sensor: msg.sensorData.id,
temperature: msg.sensorData.temp,
humidity: msg.sensorData.humidity,
battery: msg.sensorData.battery
};
msg.payload = JSON.stringify(entry) + '\n';
msg.logDate = msg.timestamp.toISOString().split('T')[0];
return msg;
Daily Router (Function):
// Use date-based file names
msg.nodeId = `/ns1:SensorLogs/ns1:data-${msg.logDate}.jsonl`;
// Check if we need to archive yesterday's file
const lastLogDate = flow.get('lastLogDate');
if (lastLogDate && lastLogDate !== msg.logDate) {
// New day - trigger archive
const archiveMsg = {
nodeId: `/ns1:SensorLogs/ns1:data-${lastLogDate}.jsonl`,
archiveDate: lastLogDate
};
node.send([msg, archiveMsg]);
} else {
node.send([msg, null]);
}
flow.set('lastLogDate', msg.logDate);
return;
Write Log (OPC UA File Operation):
- Mode: WriteAppend
- NodeId: (set by msg.nodeId)
- Encoding: utf8
Tips and Best Practices
-
Choose the Right Mode:
- Use Write for complete file replacement (configs, reports)
- Use WriteAppend for logging and incremental data
-
Validate Input Data:
- Always validate msg.payload before writing
- Handle undefined, null, or empty values
- Check data types match expected format
-
Handle Errors Gracefully:
- Add Catch nodes for all write operations
- Implement retry logic for transient failures
- Store failed writes for manual review
-
Consider File Size:
- The node handles chunking automatically
- Monitor disk space on the OPC UA server
- Implement log rotation for continuous logging
-
Use Atomic Writes:
- Write to a temporary file first
- Rename/move after successful write
- Prevents corruption if write fails mid-operation
-
Encoding Matters:
- Always specify encoding for text files
- Use
nonefor binary data - Match encoding to target system requirements
-
Security:
- Verify write permissions before operations
- Never write user input directly without validation
- Log all write operations for audit trails
Related Operations
- Reading Files - Learn how to read data from files
- Appending to Files - More details on append operations
- Browse Files - Discover available files on the server
Troubleshooting
Problem: "nothing to write" error
Solution: Ensure msg.payload is not undefined or empty. Check your function node output.
Problem: "BadNotWritable" status code
Solution: The file or server doesn't allow writes. Check:
- File permissions on the OPC UA server
- User access rights in your OPC UA connection
- File is not locked by another process
Problem: Written file contains "[object Object]"
Solution: You're writing an object without JSON.stringify. Either:
- Let the node auto-stringify (it should do this automatically)
- Manually stringify in a function node:
msg.payload = JSON.stringify(msg.payload)
Problem: Special characters appear corrupted
Solution: Wrong encoding. Try:
- Check the source encoding of your data
- Match the encoding setting to your target system
- Use UTF-8 for international characters
Problem: Write succeeds but file is empty
Solution: Check that msg.payload actually contains data:
- Add a Debug node before the File Operation node
- Verify msg.payload is not null, undefined, or empty string
- Check if payload type is supported (String, Buffer, Object, Number, Boolean)