Apache Camel: File Component
The Camel File component polls a directory for new files, processes them inside a route, and supports moving, deleting, or renaming files after processing: all with no boilerplate Java IO code.
Contents
Polling a Directory
Use from("file:<directory>?...")java.io.File
import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;
@Component
public class FilePollerRoute extends RouteBuilder {
@Override
public void configure() {
from("file:data/inbox"
+ "?delay=5000" // poll every 5 seconds
+ "&initialDelay=1000" // wait 1s before the first poll
+ "&recursive=false") // don't descend into subdirectories
.routeId("file-poller")
.convertBodyTo(String.class) // read file content as String
.log("File: ${header.CamelFileName} — Content: ${body}");
}
}
noop=trueMove & Delete After Processing
Control what happens to a file once it has been processed successfully using the movedeletemoveFailed
@Component
public class FileProcessorRoute extends RouteBuilder {
@Override
public void configure() {
// Move processed files to a .done subdirectory
from("file:data/inbox?move=.done")
.log("Processed and moved: ${header.CamelFileName}");
// Delete processed files
from("file:data/inbox?delete=true")
.log("Processed and deleted: ${header.CamelFileName}");
// Move successfully processed to .done, failed to .error
from("file:data/inbox?move=.done&moveFailed=.error")
.log("Processing: ${header.CamelFileName}")
.process(exchange -> {
String content = exchange.getMessage().getBody(String.class);
if (content.contains("INVALID")) {
throw new RuntimeException("Invalid file content");
}
});
}
}
moveFailedmoveFailedFile Headers
Camel populates several CamelFile*
| Header | Type | Description |
|---|---|---|
CamelFileName | String | File name (e.g. orders.csv |
CamelFileNameOnly | String | File name without path. |
CamelFileAbsolutePath | String | Full absolute path to the file. |
CamelFileLength | Long | File size in bytes. |
CamelFileLastModified | Long | Last modified timestamp in milliseconds since epoch. |
CamelFileParent | String | Parent directory path. |
from("file:data/inbox?delete=true")
.process(exchange -> {
String name = exchange.getMessage().getHeader("CamelFileName", String.class);
Long size = exchange.getMessage().getHeader("CamelFileLength", Long.class);
Long modified = exchange.getMessage().getHeader("CamelFileLastModified", Long.class);
System.out.printf("File: %s Size: %d bytes Modified: %d%n", name, size, modified);
});
Writing Files
Use to("file:<directory>?...")CamelFileNamefileName
@Component
public class FileWriterRoute extends RouteBuilder {
@Override
public void configure() {
// Write body to outbox — Camel generates a unique filename
from("direct:writeFile")
.to("file:data/outbox");
// Set the output filename via header
from("direct:writeNamed")
.setHeader("CamelFileName", simple("output-${date:now:yyyyMMdd-HHmmss}.txt"))
.to("file:data/outbox");
// Read-only: poll but don't move or delete (noop=true)
from("file:data/reference?noop=true&delay=60000")
.log("Reference file: ${header.CamelFileName}");
// Append to an existing file
from("direct:appendLog")
.setHeader("CamelFileName", constant("audit.log"))
.to("file:data/logs?fileExist=Append");
}
}
fileExistOverrideAppendFailIgnoreFiltering Files
Use the includeexcludeGenericFileFilter
@Component
public class FilteredFileRoute extends RouteBuilder {
@Override
public void configure() {
// Only process .csv files
from("file:data/inbox?include=.*\\.csv")
.log("Processing CSV: ${header.CamelFileName}");
// Exclude backup and temp files
from("file:data/inbox?exclude=.*\\.(bak|tmp|log)")
.log("Processing: ${header.CamelFileName}");
// Ant-style pattern (alternative to regex)
from("file:data/inbox?antInclude=orders/**/*.xml")
.log("Processing order XML: ${header.CamelFileName}");
}
}
import org.apache.camel.component.file.GenericFile;
import org.apache.camel.component.file.GenericFileFilter;
// Custom filter: only process files larger than 1 KB
public class SizeFilter implements GenericFileFilter<java.io.File> {
@Override
public boolean accept(GenericFile<java.io.File> file) {
return file.getFileLength() > 1024;
}
}
// Register and use the custom filter
from("file:data/inbox?filter=#sizeFilter")
.log("Large file: ${header.CamelFileName}");
// In a @Configuration class:
// @Bean public SizeFilter sizeFilter() { return new SizeFilter(); }
Idempotent Consumer
The idempotent=true
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.file.FileIdempotentRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import java.io.File;
@Component
public class IdempotentFileRoute extends RouteBuilder {
@Override
public void configure() {
// In-memory idempotent repository (default — resets on restart)
from("file:data/inbox?noop=true&idempotent=true")
.log("Processing (once): ${header.CamelFileName}");
// File-backed idempotent repository (persists across restarts)
from("file:data/inbox"
+ "?noop=true"
+ "&idempotent=true"
+ "&idempotentRepository=#fileRepo"
+ "&idempotentKey=${header.CamelFileName}-${header.CamelFileLastModified}")
.log("Processing (persistent idempotent): ${header.CamelFileName}");
}
}
@Configuration
class FileRepoConfig {
@Bean
public FileIdempotentRepository fileRepo() {
// Store seen file names in a local file
FileIdempotentRepository repo = FileIdempotentRepository.fileIdempotentRepository(
new File("data/.idempotent.store")
);
repo.setMaxFileStoreSize(5_000_000L); // 5 MB max
return repo;
}
}
idempotentKey