Backend Development 16 min read

A Reactive Download Library for Spring MVC and WebFlux: Design, Implementation, and Usage

This article introduces a Java library that simplifies file download in Spring applications by using a @Download annotation, supporting various source types, reactive and servlet environments, concurrent loading, compression, and customizable handlers, and details its architecture, code examples, and practical considerations.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
A Reactive Download Library for Spring MVC and WebFlux: Design, Implementation, and Usage

The author presents a library that abstracts and simplifies download functionality in Java Spring projects, allowing a single @Download annotation to handle various data sources such as classpath files, local files, HTTP URLs, strings, or custom objects.

Basic Idea

By annotating a controller method with @Download (e.g., @Download(source = "classpath:/download/README.txt") ) the framework automatically resolves the source, determines the file name (via @SourceName), and streams the content to the client.

Key Code Example

@Download(source = "classpath:/download/README.txt")
@GetMapping("/classpath")
public void classpath() { }

@Download
@GetMapping("/file")
public File file() {
    return new File("/Users/Shared/README.txt");
}

@Download
@GetMapping("/http")
public String http() {
    return "http://127.0.0.1:8080/concept-download/image.jpg";
}

The library is especially useful for complex scenarios such as exporting a zip of device QR‑code images. The author describes a real requirement where each device has a QR‑code URL, and the download process must:

Fetch the device list.

Download each QR‑code image (with caching).

Perform concurrent downloads for performance.

Compress all images into a zip file.

Write the zip to the HTTP response.

Instead of writing ~200 lines of repetitive code, the library lets the developer simply return List from a controller method annotated with @Download, and the framework handles the whole pipeline.

Architecture Overview

The design follows a reactive pipeline similar to Spring Cloud Gateway. The main components are:

DownloadContext : a shared context passed through all processing steps.

DownloadHandler and DownloadHandlerChain : each step (e.g., source loading, compression) implements handle and delegates to the next handler.

Source hierarchy (e.g., FileSource , HttpSource ) created by SourceFactory implementations that decide whether they can handle a given object.

SourceLoader : loads remote resources concurrently, customizable via thread pools or schedulers.

SourceCompressor : compresses the loaded sources; the default is a zip compressor but any format can be added.

DownloadWriter : abstracts writing bytes to the response, supporting range requests, charset handling, and progress callbacks.

DownloadResponse implementations ( ReactiveDownloadResponse for WebFlux and a servlet version) hide the differences between HttpServletResponse and ServerHttpResponse .

Event publishing ( DownloadEventPublisher and DownloadEventListener ) enables flexible logging of each stage, progress updates, and total time spent.

Reactive Compatibility

To work with both Spring MVC and WebFlux, the library uses a WebFilter ( ReactiveDownloadFilter ) that stores the current ServerHttpRequest and ServerHttpResponse in the Reactor Context . Handlers retrieve them via ReactiveDownloadHolder.getRequest() and getResponse() . Because WebFlux is non‑blocking, the response is written using a custom FluxSinkOutputStream that converts written byte[] into DataBuffer objects.

public class ReactiveDownloadFilter implements WebFilter {
    @Override
    public Mono
filter(ServerWebExchange exchange, WebFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        return chain.filter(exchange)
            .contextWrite(ctx -> ctx.put(ServerHttpRequest.class, request))
            .contextWrite(ctx -> ctx.put(ServerHttpResponse.class, response));
    }
}

public class ReactiveDownloadResponse implements DownloadResponse {
    private final ServerHttpResponse response;
    private OutputStream os;
    private Mono
mono;
    // write method creates a FluxSinkOutputStream that pushes bytes to the response
}

Handling Custom Objects

Developers can annotate fields with @SourceObject (the actual data to download) and @SourceName (the desired file name). By marking a class with @SourceModel , the library uses reflection to extract these values and creates the appropriate Source instance.

public class Device {
    private String name;
    @SourceObject
    private String qrCodeUrl; // HTTP address of the QR code image
    @SourceName
    public String getQrCodeName() { return name + ".png"; }
}

Logging and Pitfalls

The author notes several challenges: mixing reactive and servlet APIs, ensuring the download context is destroyed after response writing (using doAfterTerminate ), and handling back‑pressure when converting a FluxSink to an OutputStream . Detailed logs for each stage helped discover many bugs.

In conclusion, the library provides a concise, annotation‑driven way to implement complex download scenarios—such as exporting zipped QR‑code images—while remaining flexible, extensible, and compatible with both Spring MVC and WebFlux.

JavaBackend DevelopmentSpringfile downloadReactiveAnnotationcompression
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.