> Markdown version of [Handle File Downloads](https://vaadin.com/docs/next/building-apps/forms-data/handle-downloads). Section index: [llms.txt](https://vaadin.com/docs/next/building-apps/llms.txt)

# Handle File Downloads

This article shows how to serve files for download from a Vaadin application using `DownloadHandler` and the `Anchor` component. It covers generated content, files from disk, and service-backed downloads. For progress tracking, inline rendering, and advanced features, see the [Downloads](https://vaadin.com/docs/next/flow/advanced/downloads.md) reference.

## <a id="copy-paste-into-your-project"></a>Copy-Paste into Your Project

A self-contained view that generates and serves a CSV file for download:

```java
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.streams.DownloadHandler;
import com.vaadin.flow.server.streams.DownloadResponse;

@Route("download-example")
public class DownloadExampleView extends VerticalLayout {

    public DownloadExampleView() {
        Anchor downloadLink = new Anchor( // (1)
                DownloadHandler.fromInputStream(event -> { // (2)
                    try {
                        byte[] csv = generateCsv(); // (3)
                        return new DownloadResponse( // (4)
                                new ByteArrayInputStream(csv),
                                "report.csv",
                                "text/csv",
                                csv.length);
                    } catch (Exception e) {
                        return DownloadResponse.error(500); // (5)
                    }
                }), "Download Report");

        add(downloadLink);
    }

    private byte[] generateCsv() {
        // Replace with your report generation logic
        String csv = "Name,Email\nAlice,alice@example.com\n";
        return csv.getBytes(StandardCharsets.UTF_8);
    }
}
```

1. `Anchor` renders a regular HTML link. The browser handles the download natively — no JavaScript needed.

2. `DownloadHandler.fromInputStream()` accepts a callback that runs when the user clicks the link.

3. Generates the content. Replace this with your own logic.

4. `DownloadResponse` bundles the `InputStream`, filename, content type, and content length.

5. Returns an error response if content generation fails.

> **Note: Memory usage**
>
> This example builds the entire file in memory as a `byte[]` before serving it. This is fine for small files and low concurrency. For large files or many concurrent users, have your service return an `InputStream` that streams content directly — see [Fetching Content from a Service](#fetching-content-from-a-service).

## <a id="downloading-a-file-from-disk"></a>Downloading a File from Disk

When a file already exists on the server’s filesystem, use `DownloadHandler.forFile()`:

```java
Anchor download = new Anchor(
        DownloadHandler.forFile(
                new File("/data/exports/report.pdf")),
        "Download Report");
```

The filename and content type are inferred from the `File` object.

## <a id="fetching-content-from-a-service"></a>Fetching Content from a Service

In a typical application, the download content comes from a Spring service — generating a report, reading from a database, or fetching from cloud storage. Inject the service through the constructor and have it return an `InputStream` so that content is streamed directly to the browser without buffering the entire file in server memory:

```java
@Route("report-download")
public class ReportDownloadView extends VerticalLayout {

    public ReportDownloadView(ReportService reportService) {
        Anchor downloadLink = new Anchor(
                DownloadHandler.fromInputStream(event -> {
                    try {
                        return new DownloadResponse(
                                reportService.generateMonthlyReport(), // (1)
                                "monthly-report.pdf",
                                "application/pdf",
                                -1); // (2)
                    } catch (Exception e) {
                        return DownloadResponse.error(500);
                    }
                }), "Download Monthly Report");

        add(downloadLink);
    }
}
```

1. The service returns an `InputStream`. The content is streamed directly to the client without loading it all into memory first.

2. Pass `-1` when the content length isn’t known in advance. The browser won’t show a progress percentage, but memory usage stays low regardless of file size.

## <a id="when-to-use-each-approach"></a>When to Use Each Approach

- **`forFile()`** — the file already exists on disk. Simplest option, handles content type and filename automatically.

- **`fromInputStream()`** — content is generated dynamically or comes from a database, external API, or cloud storage. This is the most common approach.

- **Lambda with `DownloadEvent`** — you need full control over the response, such as setting custom headers, writing directly to the output stream, or updating the UI after the download completes.

## <a id="beyond-the-basics"></a>Beyond the Basics

This article covers the fundamentals, but Vaadin’s download API offers more:

- **Progress tracking** — show a progress bar during large downloads using the fluent `whenStart()`, `onProgress()`, and `whenComplete()` callbacks

- **Inline rendering** — display content in the browser instead of downloading it by calling `inline()` on the handler

- **Static resources** — serve files from the classpath or servlet context with `forClassResource()` and `forServletResource()`

- **Custom headers** — set cache control, content disposition, and other response headers using the `DownloadEvent` API

See the [Downloads](https://vaadin.com/docs/next/flow/advanced/downloads.md) reference for details on these features.
