File Handling
Comprehensive file and directory operations using BoxLang's NIO-powered file system API
BoxLang provides a powerful and intuitive API for file and directory operations, built on Java's modern NIO (New I/O) packages for optimal performance and reliability. Whether you need to read configuration files, process large datasets, manage directory structures, or handle file uploads, BoxLang offers both function-based (BIFs) and component-based approaches to suit your coding style.
🚀 Core Features
NIO-Powered - Built on Java's
java.nio.filefor modern, high-performance I/OClosure/Lambda Support - Filter and transform with functional programming
Stream Processing - Handle large files efficiently with streams
Cross-Platform - Consistent behavior across Windows, Linux, and macOS
Path Expansion - Automatic resolution of relative and absolute paths
Charset Support - Full Unicode and encoding support
📁 File Operations
BoxLang provides comprehensive file manipulation capabilities through global functions (BIFs) and the <bx:file> component.
Reading Files
Read entire file contents into memory:
// Simple read
content = fileRead( "/path/to/config.txt" )
// With specific charset
content = fileRead( "/path/to/data.txt", "UTF-8" )
// Read binary data
binaryData = fileReadBinary( "/path/to/image.jpg" )
// Using component (script)
bx:file action="read" file="/path/to/file.txt" variable="fileContent";<!-- Using component (template) -->
<bx:file action="read" file="/path/to/file.txt" variable="fileContent">Reference: fileRead()
Writing Files
Write content to files with automatic path creation:
// Simple write
fileWrite( "/path/to/output.txt", "Hello BoxLang!" )
// Write with charset
fileWrite( "/path/to/output.txt", "Hello BoxLang!", "UTF-8" )
// Write with path creation
fileWrite( "/deep/nested/path/file.txt", "data", "UTF-8", true )
// Write binary
fileWrite( "/path/to/image.jpg", binaryData )
// Using component (script)
bx:file
action="write"
file="/path/to/file.txt"
output="Hello BoxLang!";<!-- Using component (template) -->
<bx:file
action="write"
file="/path/to/file.txt"
output="Hello BoxLang!">Reference: fileWrite()
Appending to Files
Add content to existing files:
// Append string content
fileAppend( "/path/to/log.txt", "New log entry" )
// Using open file handle
file = fileOpen( "/path/to/log.txt", "append" )
fileAppend( file, "Entry 1" )
fileAppend( file, "Entry 2" )
fileClose( file )
// Using component (script)
bx:file
action="append"
file="/path/to/log.txt"
output="Additional content";<!-- Using component (template) -->
<bx:file
action="append"
file="/path/to/log.txt"
output="Additional content">Reference: fileAppend()
File Operations
Common file management operations:
// Check if file exists
if( fileExists( "/path/to/file.txt" ) ){
// File operations
}
// Copy file
fileCopy( "/source/file.txt", "/destination/file.txt" )
// Move/Rename file
fileMove( "/old/location.txt", "/new/location.txt" )
// Delete file
fileDelete( "/path/to/file.txt" )
// Get file info
info = fileInfo( "/path/to/file.txt" )
// Returns: { name, path, size, type, dateLastModified, attributes, mode }
// Get MIME type
mimeType = fileGetMimeType( "/path/to/document.pdf" )
// Set file attributes (Unix/Linux)
fileSetAccessMode( "/path/to/script.sh", "755" )
// Set last modified time
fileSetLastModified( "/path/to/file.txt", now() )Reference: fileExists(), fileCopy(), fileMove(), fileDelete(), fileInfo()
Working with File Handles
For fine-grained control, use file handles:
// Open file for reading
file = fileOpen( "/path/to/data.txt", "read" )
// Read line by line
while( !fileIsEOF( file ) ){
line = fileReadLine( file )
// Process line
}
fileClose( file )
// Open file for writing
file = fileOpen( "/path/to/output.txt", "write", "UTF-8" )
fileWriteLine( file, "First line" )
fileWriteLine( file, "Second line" )
fileClose( file )
// Seekable file operations
file = fileOpen( "/path/to/data.bin", "read", "UTF-8", true )
fileSeek( file, 100 ) // Jump to position 100
data = fileRead( file )
fileClose( file )Reference: fileOpen(), fileClose(), fileReadLine(), fileWriteLine(), fileIsEOF(), fileSeek()
File Upload Handling
Handle HTTP file uploads securely in web runtimes.
Web Runtime Only: File upload functions (fileUpload(), fileUploadAll()) are only available in web runtimes (Servlet, MiniServer). They are not available in CLI or other runtime contexts.
<form method="post" enctype="multipart/form-data">
<input type="file" name="uploadedFile">
<button type="submit">Upload</button>
</form>if( structKeyExists( form, "uploadedFile" ) ){
try {
// Upload with MIME type validation
result = fileUpload(
getTempDirectory(),
"uploadedFile",
"image/jpeg,image/png",
"makeUnique"
)
// Verify file extension (MIME types can be spoofed)
if( !listFindNoCase( "jpg,jpeg,png", result.serverFileExt ) ){
throw( "Invalid file type" )
}
// Process uploaded file
writeOutput( "Uploaded: #result.serverFile#" )
} catch( any e ){
writeOutput( "Upload failed: #e.message#" )
}
}
// Upload multiple files
results = fileUploadAll( getTempDirectory(), "image/*" )
results.each( function( uploadResult ){
// Process each uploaded file
} )Reference: fileUpload(), fileUploadAll()
Temporary Files
Create and manage temporary files:
// Get system temp directory
tempDir = getTempDirectory()
// Create temporary file
tempFile = createTempFile( getTempDirectory(), "myapp" )
// Use temp file
fileWrite( tempFile, "temporary data" )
// Cleanup happens automatically or manually
fileDelete( tempFile )Reference: getTempDirectory(), createTempFile(), createTempDirectory()
📂 Directory Operations
Manage directory structures with powerful listing, filtering, and manipulation capabilities.
Basic Directory Management
// Check if directory exists
if( directoryExists( "/path/to/dir" ) ){
// Directory operations
}
// Create directory
directoryCreate( "/path/to/new/directory" )
// Create nested directories
directoryCreate( "/deep/nested/path", true ) // createPath=true
// Copy directory
directoryCopy( "/source/dir", "/destination/dir" )
// Copy recursively
directoryCopy( "/source/dir", "/destination/dir", true )
// Move/Rename directory
directoryMove( "/old/path", "/new/path" )
// Delete directory
directoryDelete( "/path/to/dir" )
// Delete recursively
directoryDelete( "/path/to/dir", true )
// Using component (script)
bx:directory action="create" directory="/path/to/new/dir";
bx:directory action="delete" directory="/path/to/dir" recurse="true";<!-- Using component (template) -->
<bx:directory action="create" directory="/path/to/new/dir">
<bx:directory action="delete" directory="/path/to/dir" recurse="true">Reference: directoryExists(), directoryCreate(), directoryCopy(), directoryMove(), directoryDelete()
Listing Directory Contents
BoxLang provides flexible directory listing with multiple return formats:
// Simple list - returns array of absolute paths
files = directoryList( "/path/to/dir" )
// List names only
names = directoryList(
path = "/path/to/dir",
listInfo = "name"
)
// List as query with full details
query = directoryList(
path = "/path/to/dir",
listInfo = "query"
)
// Query columns: name, size, type, dateLastModified, attributes, mode, directory
// Recursive listing
allFiles = directoryList(
path = "/path/to/dir",
recurse = true
)
// Filter by type
filesOnly = directoryList(
path = "/path/to/dir",
type = "file"
)
dirsOnly = directoryList(
path = "/path/to/dir",
type = "dir"
)
// Sort results
sorted = directoryList(
path = "/path/to/dir",
sort = "name asc"
)
// Available sort columns: name, size, date, type
// Sort directions: asc, descReference: directoryList()
Filtering with Closures/Lambdas
Use functional programming for powerful filtering:
// Filter with lambda - only .txt files
textFiles = directoryList(
path = "/documents",
filter = ( path ) => path.endsWith( ".txt" )
)
// Filter with closure - files modified today
todayFiles = directoryList(
path = "/uploads",
listInfo = "query",
filter = ( path ) => {
info = fileInfo( path )
return info.dateLastModified.dateCompare( now(), "d" ) == 0
}
)
// Filter by size - files larger than 1MB
largeFiles = directoryList(
path = "/data",
listInfo = "query",
filter = ( path ) => {
info = fileInfo( path )
return info.size > 1048576
}
)
// Complex filtering - specific file patterns
configFiles = directoryList(
path = "/config",
recurse = true,
filter = ( path ) => {
fileName = getFileFromPath( path )
return fileName.startsWith( "app-" ) && fileName.endsWith( ".json" )
}
)Glob Pattern Filtering
Use glob patterns for simple filtering:
// Single pattern
txtFiles = directoryList(
path = "/documents",
filter = "*.txt"
)
// Multiple patterns with pipe separator
mediaFiles = directoryList(
path = "/media",
filter = "*.jpg|*.png|*.gif"
)
// Pattern matching
configFiles = directoryList(
path = "/config",
filter = "app-*.json"
)Advanced Directory Queries
// Get detailed directory information
query = directoryList(
path = "/projects",
recurse = true,
listInfo = "query",
type = "file",
sort = "size desc"
)
// Process query results
query.each( function( row ){
println( "File: #row.name# - Size: #row.size# bytes" )
} )
// Filter query results
largeFiles = query.filter( function( row ){
return row.size > 1000000 // Files > 1MB
} )
// Sum total size
totalSize = query.reduce( function( sum, row ){
return sum + row.size
}, 0 )Path Utilities
Work with file system paths:
// Expand relative paths to absolute
absolutePath = expandPath( "../config/settings.json" )
// Get canonical path (resolves symbolic links)
canonicalPath = getCanonicalPath( "/path/with/symlinks" )
// Contract absolute path to relative
relativePath = contractPath( "/full/path/to/file.txt" )
// Extract directory from full path
directory = getDirectoryFromPath( "/path/to/file.txt" )
// Returns: /path/to/
// Check free space on partition
freeBytes = getFreeSpace( "/data/partition" )Reference: expandPath(), getCanonicalPath(), contractPath(), getDirectoryFromPath()
🌊 Dealing With Large Files
When working with large files, loading entire contents into memory can cause performance issues. BoxLang leverages Java NIO streams for efficient processing of large files. Through BoxLang's 100% Java interoperability, you can directly use Java's powerful Stream API for advanced file processing.
Stream-Based File Reading
Process files line-by-line without loading everything into memory:
// Open file for line-by-line reading
file = fileOpen( "/path/to/large-file.log", "read" )
try {
while( !fileIsEOF( file ) ){
line = fileReadLine( file )
// Process each line
if( line.contains( "ERROR" ) ){
// Handle error line
writeLog( line )
}
}
} finally {
fileClose( file )
}Using Java Streams for Advanced Processing
BoxLang's Java interoperability allows you to use Java NIO Streams directly for powerful file processing:
// Import Java classes
import java:java.nio.file.Files;
import java:java.nio.file.Paths;
import java:java.util.stream.Collectors;
// Process large file with Java Streams
path = Paths.get( "/path/to/large-data.csv" )
stream = Files.lines( path )
try {
// Filter and transform lines using Java Streams
results = stream
.filter( ( line ) => line.contains( "active" ) )
.map( ( line ) => listToArray( line ) )
.collect( Collectors.toList() )
} finally {
stream.close()
}Parallel File Processing
Process large files using multiple threads with parallel streams:
import java:java.nio.file.Files;
import java:java.nio.file.Paths;
// Parallel stream processing
path = Paths.get( "/path/to/huge-file.log" )
stream = Files.lines( path ).parallel()
try {
// Process lines in parallel
errorCount = stream
.filter( ( line ) => line.contains( "ERROR" ) )
.count()
println( "Found #errorCount# errors" )
} finally {
stream.close()
}Chunked Reading
Read files in chunks for controlled memory usage:
// Read file in chunks
file = fileOpen( "/path/to/binary-data.bin", "read" )
chunkSize = 8192 // 8KB chunks
try {
while( !fileIsEOF( file ) ){
chunk = fileRead( file, chunkSize )
// Process chunk
processChunk( chunk )
}
} finally {
fileClose( file )
}Processing Large CSV Files
Efficient CSV processing without loading entire file:
file = fileOpen( "/path/to/large-dataset.csv", "read" )
lineNumber = 0
try {
// Skip header
header = fileReadLine( file )
// Process data rows
while( !fileIsEOF( file ) ){
lineNumber++
line = fileReadLine( file )
// Parse CSV line
data = listToArray( line )
// Process record
processRecord( data )
// Progress indicator every 1000 rows
if( lineNumber % 1000 == 0 ){
println( "Processed #lineNumber# rows" )
}
}
} finally {
fileClose( file )
}Alternative using Java Streams:
import java:java.nio.file.Files;
import java:java.nio.file.Paths;
path = Paths.get( "/path/to/large-dataset.csv" )
stream = Files.lines( path ).skip( 1 ) // Skip header
try {
stream.forEach( ( line ) => {
data = listToArray( line )
processRecord( data )
} )
} finally {
stream.close()
}Streaming Log File Analysis
Analyze large log files efficiently using Java Streams:
import java:java.nio.file.Files;
import java:java.nio.file.Paths;
import java:java.util.stream.Collectors;
import java:java.util.function.Function;
// Analyze log patterns without loading entire file
path = Paths.get( "/var/log/application.log" )
stream = Files.lines( path )
try {
// Group errors by type
errorCounts = stream
.filter( ( line ) => line.contains( "[ERROR]" ) )
.map( ( line ) => {
// Extract error type
matches = line.reMatch( "(?<=\[ERROR\]\s)[^:]*" )
return matches.len() > 0 ? matches[ 1 ] : "Unknown"
} )
.collect(
Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)
)
// Display results
errorCounts.forEach( ( type, count ) => {
println( "#type#: #count# occurrences" )
} )
} finally {
stream.close()
}
### Memory-Efficient File Copying
Copy large files with controlled buffer size:
```js
// Copy large file with custom buffer
source = fileOpen( "/source/large-file.dat", "read" )
destination = fileOpen( "/destination/large-file.dat", "write" )
try {
bufferSize = 65536 // 64KB buffer
while( !fileIsEOF( source ) ){
chunk = fileRead( source, bufferSize )
fileWrite( destination, chunk )
}
} finally {
fileClose( source )
fileClose( destination )
}Performance Tip: For files larger than 100MB, always use stream-based processing or chunked reading to avoid memory issues. BoxLang's Java interoperability gives you direct access to Java NIO Streams for powerful functional operations while maintaining low memory footprint.
🔐 Best Practices
Security Considerations
// Validate file paths to prevent directory traversal
function sanitizePath( userPath ){
// Remove potentially dangerous patterns
safePath = userPath
.replace( "..", "" )
.replace( "~", "" )
// Ensure path is within allowed directory
basePath = expandPath( "/uploads" )
fullPath = expandPath( safePath )
if( !fullPath.startsWith( basePath ) ){
throw( "Invalid file path" )
}
return fullPath
}
// Validate uploaded files
if( structKeyExists( form, "upload" ) ){
result = fileUpload( getTempDirectory(), "upload" )
// Check file extension
allowedExtensions = "jpg,png,gif,pdf"
if( !listFindNoCase( allowedExtensions, result.serverFileExt ) ){
fileDelete( result.serverDirectory & result.serverFile )
throw( "File type not allowed" )
}
// Check file size
maxSize = 5 * 1024 * 1024 // 5MB
if( result.fileSize > maxSize ){
fileDelete( result.serverDirectory & result.serverFile )
throw( "File too large" )
}
}Error Handling
// Robust file operations with error handling
try {
if( !fileExists( configPath ) ){
throw(
type = "FileNotFound",
message = "Configuration file not found: #configPath#"
)
}
content = fileRead( configPath )
config = deserializeJSON( content )
} catch( "FileNotFound" e ){
// Create default config
config = getDefaultConfig()
fileWrite( configPath, serializeJSON( config ) )
} catch( any e ){
// Log error and use fallback
writeLog(
type = "error",
text = "Config load failed: #e.message#"
)
config = getDefaultConfig()
}Resource Management
// Always close file handles
file = fileOpen( "/path/to/file.txt", "write" )
try {
fileWrite( file, "data" )
} finally {
// Ensures file is closed even if error occurs
fileClose( file )
}
// Use try-finally for cleanup
tempFile = createTempFile( getTempDirectory(), "process" )
try {
// Process with temp file
fileWrite( tempFile, processData() )
result = analyzeFile( tempFile )
} finally {
// Always cleanup temp files
if( fileExists( tempFile ) ){
fileDelete( tempFile )
}
}Cross-Platform Paths
// Use forward slashes - BoxLang handles conversion
goodPath = "/path/to/file.txt" // ✓ Works everywhere
badPath = "\path\to\file.txt" // ✗ Windows-specific
// Let BoxLang expand paths
relativePath = "../config/app.json"
absolutePath = expandPath( relativePath )
// Use path functions for manipulation
directory = getDirectoryFromPath( absolutePath )
fileName = getFileFromPath( absolutePath )📚 Function Reference
File Functions
fileRead()
Read entire file contents
fileReadBinary()
Read file as binary data
fileWrite()
Write content to file
fileAppend()
Append content to file
fileOpen()
Open file handle
fileClose()
Close file handle
fileReadLine()
Read single line
fileWriteLine()
Write single line
fileCopy()
Copy file
fileMove()
Move/rename file
fileDelete()
Delete file
fileExists()
Check file existence
fileInfo()
Get file metadata
fileIsEOF()
Check end of file
fileSeek()
Set file pointer position
fileGetMimeType()
Get file MIME type
fileSetAccessMode()
Set Unix file permissions
fileSetAttribute()
Set file attributes
fileSetLastModified()
Set modification time
fileUpload()
Handle file upload
fileUploadAll()
Handle multiple uploads
createTempFile()
Create temporary file
Directory Functions
directoryList()
List directory contents
directoryCreate()
Create directory
directoryDelete()
Delete directory
directoryCopy()
Copy directory
directoryMove()
Move/rename directory
directoryExists()
Check directory existence
Path Functions
expandPath()
Convert relative to absolute path
contractPath()
Convert absolute to relative path
getCanonicalPath()
Resolve symbolic links
getDirectoryFromPath()
Extract directory from path
getTempDirectory()
Get system temp directory
createTempDirectory()
Create temporary directory
getFreeSpace()
Get partition free space
🎯 Common Use Cases
Reading Configuration Files
// Load JSON config
function loadConfig( configPath ){
if( !fileExists( configPath ) ){
return {}
}
content = fileRead( configPath )
return deserializeJSON( content )
}
config = loadConfig( expandPath( "./config/app.json" ) )Writing Log Files
// Append to daily log file
function writeLog( message ){
logDir = expandPath( "./logs" )
if( !directoryExists( logDir ) ){
directoryCreate( logDir )
}
logFile = logDir & "/" & dateFormat( now(), "yyyy-mm-dd" ) & ".log"
timestamp = dateTimeFormat( now(), "yyyy-mm-dd HH:nn:ss" )
logEntry = "[#timestamp#] #message#" & chr( 10 )
fileAppend( logFile, logEntry )
}
writeLog( "Application started" )Batch File Processing
// Process all JSON files in directory
files = directoryList(
path = "/data/input",
filter = "*.json"
)
results = []
files.each( function( filePath ){
try {
data = deserializeJSON( fileRead( filePath ) )
processed = processData( data )
results.append( processed )
} catch( any e ){
writeLog( "Error processing #filePath#: #e.message#" )
}
} )Creating Directory Archives
// Collect files for archiving
filesToArchive = directoryList(
path = "/documents",
recurse = true,
filter = ( path ) => {
info = fileInfo( path )
// Files older than 30 days
return info.dateLastModified.dateAdd( "d", 30 ) < now()
}
)
// Process files
filesToArchive.each( function( file ){
// Move to archive
archivePath = "/archive" & file.replace( "/documents", "" )
directory = getDirectoryFromPath( archivePath )
if( !directoryExists( directory ) ){
directoryCreate( directory, true )
}
fileMove( file, archivePath )
} )Last updated
Was this helpful?
