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>?...") to watch a folder and trigger the route for each new file. By default Camel reads the file as a java.io.File: convert to String for text processing.

Java
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}");
    }
}
By default Camel locks the file while processing it to prevent double-processing. The lock is released once the route completes. Use noop=true if you only want to read without touching the file.

Move & Delete After Processing

Control what happens to a file once it has been processed successfully using the move, delete, and moveFailed options.

Java
@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");
                }
            });
    }
}
If your route throws an exception and you have moveFailed set, Camel moves the original file there. Without moveFailed, the file stays in the inbox and will be picked up again on the next poll: potentially causing an infinite loop.

File Headers

Camel populates several CamelFile* headers from the file metadata when a file is consumed. Use them inside route logic and log statements.

HeaderTypeDescription
CamelFileNameStringFile name (e.g. orders.csv).
CamelFileNameOnlyStringFile name without path.
CamelFileAbsolutePathStringFull absolute path to the file.
CamelFileLengthLongFile size in bytes.
CamelFileLastModifiedLongLast modified timestamp in milliseconds since epoch.
CamelFileParentStringParent directory path.
Java
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>?...") to write the exchange body to a file. Control the output file name via the CamelFileName header or the fileName URI option.

Java
@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");
    }
}
The fileExist option controls what happens when the target file already exists: Override (default), Append, Fail, or Ignore.

Filtering Files

Use the include and exclude options (regex) to filter which files are picked up, or implement a custom GenericFileFilter for complex logic.

Java
@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}");
    }
}
Java
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;
    }
}
Java
// 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 option prevents re-processing files that have already been handled. Camel tracks processed file names using a configurable repository.

Java
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;
    }
}
The idempotentKey expression determines what uniquely identifies a file. Using both the file name and last-modified timestamp means the file will be re-processed if it is updated, which is usually the desired behaviour.