Backend Development 14 min read

Design and Implementation of a Flexible Download Library for Spring MVC and WebFlux

This article explains how a Java download library uses a @Download annotation, reactive programming, handler chains, source abstractions, compression, and event logging to simplify downloading various resources—including files, HTTP URLs, and custom objects—while supporting both Spring MVC and WebFlux with concurrent processing and customizable pipelines.

Architecture Digest
Architecture Digest
Architecture Digest
Design and Implementation of a Flexible Download Library for Spring MVC and WebFlux

Downloading files is a common requirement, but handling various sources, caching, concurrent downloads, compression, and response writing can become cumbersome; the presented library solves this by introducing a simple @Download annotation that can handle any object type.

A typical scenario described is exporting device QR‑code images as a zip file: retrieve the device list, download each QR‑code image (checking cache), compress them, and write the zip to the HTTP response.

The library is built on reactive programming to support both Spring MVC and WebFlux, using AOP and a WebFilter to obtain the response object without extra parameters, and wrapping the response in a ReactiveDownloadResponse that writes bytes via a FluxSinkOutputStream .

Its architecture revolves around a DownloadHandler chain, a shared DownloadContext , and a Source abstraction (e.g., FileSource , HttpSource ) created by SourceFactory implementations; custom loading logic is provided by SourceLoader , and compression is handled by SourceCompressor implementations.

Writing to the client is abstracted by DownloadWriter , which can handle range requests, character sets, and progress callbacks, while the event system ( DownloadEventPublisher and listeners) logs each step, progress updates, and timing information.

Key code snippets illustrate the core concepts:

@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"; }
@Download(filename = "二维码.zip")
@GetMapping("/download")
public List
download() { return deviceService.all(); }
public class Device { private String name; @SourceObject private String qrCodeUrl; @SourceName public String getQrCodeName() { return name + ".png"; } }
public interface DownloadHandler extends OrderProvider { Mono
handle(DownloadContext context, DownloadHandlerChain chain); }
public interface DownloadHandlerChain { Mono
next(DownloadContext context); }
public interface SourceFactory extends OrderProvider { boolean support(Object source, DownloadContext context); Source create(Object source, DownloadContext context); }
public interface SourceLoader { Mono
load(Source source, DownloadContext context); }
public interface SourceCompressor extends OrderProvider { String getFormat(); boolean support(String format, DownloadContext context); Compression compress(Source source, DownloadWriter writer, DownloadContext context); }
public class ReactiveDownloadResponse implements DownloadResponse { private final ServerHttpResponse response; /* ... */ }
public interface DownloadWriter extends OrderProvider { boolean support(Resource resource, Range range, DownloadContext context); void write(InputStream is, OutputStream os, Range range, Charset charset, Long length, Callback callback); }
JavaSpringReactiveWebFluxfile compressiondownload
Architecture Digest
Written by

Architecture Digest

Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.

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.