> Markdown version of [File Handling](https://vaadin.com/docs/next/components/upload/file-handling). Section index: [llms.txt](https://vaadin.com/docs/next/components/llms.txt)

# Upload File Handling

The uploading of files may be handled either with Vaadin Flow using Java, or with Hilla using Lit and React. These are described in the two major sections here.

## <a id="handling-uploaded-files-in-flow"></a>Handling Uploaded Files in Flow

The Java Flow Upload component provides an API to handle directly uploaded file data, without having to set up an endpoint or a servlet. It uses an `UploadHandler` implementation to handle the incoming file data.

`UploadHandler` is a functional interface that only requires the `handleUploadRequest(UploadEvent)` to be implemented for receiving data or use one of the built-in implementations.

The following built-in implementations of `UploadHandler` are available:

- `InMemoryUploadHandler`, stores uploaded files in memory

- `FileUploadHandler`, stores uploaded files to the file system

- `TemporaryFileUploadHandler`, stores uploaded files to temporary files, typically using the system’s temporary directory. This handler is useful when dealing with large files or when memory usage needs to be minimized.

These are described in the sub-sections that follow. All of the build-in implementations extend `TransferProgressAwareHandler` and support [upload progress tracking](#upload-progress-tracking).

> **Note:** The `Receiver` had implementations for single and multiple file upload. `UploadHandler` handles both version so `Upload.setMaxFiles(1)` needs to be called manually for single-file uploads.

### <a id="custom-uploadhandler-implementations"></a>Custom UploadHandler Implementations

For more advanced use cases, you can provide custom implementations for `UploadHandler`. You might do this, for example, to upload files to cloud storage or to load data into memory first, validate and save to file.

`UploadHandler` is an `FunctionalInterface` so it can just be a lambda expression.

```java
UploadHandler uploadHandler = (event) -> {
    try (InputStream inputStream = event.getInputStream()) {
        // Process the uploaded file
        // ...
    }
    // Any exception will be caught and fire
    // a responseHandled(false, response) method call.
};
```

The `responseHandled` will write the upload status to the response. By default a success will set it to `HttpStatusCode.OK` and any failure will instead set `HttpStatusCode.INTERNAL_SERVER_ERROR`. This can be overridden when implementing the interface.

`UploadHandler` can be implemented to override default methods:

```java
public class CustomUploadHandler implements UploadHandler {
    @Override
    public void handleUploadRequest(UploadEvent event) {
        try (InputStream inputStream = event.getInputStream()) {
            // Process the uploaded file
            // ...
        }
    // Any exception will be caught and fire
    // a responseHandled(false, response) method call.
    }

    @Override
    public void responseHandled(boolean success, VaadinResponse response) {
        // Set your own custom response value for success/fail by overriding this method.
        // Default responses are 200 ok for success and 500 internal server error for failure
    }

    @Override
    public long getRequestSizeMax() {
        // Return the maximum size, in bytes, of a complete request for multipart upload.
        return -1; // -1 means no limit and is the default
    }

    @Override
    public long getFileSizeMax() {
        // Return the maximum size, in bytes, of a single file for multipart upload.
        return -1; // -1 means no limit and is the default
    }

    @Override
    public long getFileCountMax() {
        // Return the maximum amount of files allowed for multipart upload.
        return 10000; // -1 means no limit default is 10000
    }
}
```

The `responseHandled` method is called after upload has succeeded or an exception has been thrown. For multipart upload this is called after each part has succeeded.

Maximum values for requestSize, fileSize and fileCount only target multipart uploads that are iterated (multipart uploads where the type is not `multipart/form-data`).

- getRequestSizeMax sets the maximum size for the whole request.

- getFileSizeMax sets the maximum size for single files in the request.

- getFileCountMax sets the maximum amount of files in the request.

### <a id="rejecting-files-during-upload"></a>Rejecting Files During Upload

You can reject individual files during upload processing using `UploadEvent.reject()`. This is useful for validating file content after inspecting it, such as checking file headers, scanning for viruses, or verifying data format:

```java
UploadHandler uploadHandler = (event) -> {
    try (InputStream inputStream = event.getInputStream()) {
        byte[] header = inputStream.readNBytes(4);

        if (!isValidFileHeader(header)) {
            // Reject with a reason message
            event.reject("Invalid file format: expected PNG header");
            return;
        }

        // Process the valid file...
    }
};
```

You can also reject without a message by calling `event.reject()` with no arguments.

To reject uploads from the built-in handlers, without writing a custom `UploadHandler`, use a [validator](#validating-uploads) instead.

### <a id="inmemoryuploadhandler"></a>InMemoryUploadHandler

The `InMemoryUploadHandler` stores uploaded files in memory as byte arrays.

The handler is given a `successHandler` of type `InMemoryUploadCallback successHandler`. This `successHandler` is called for each successful uploaded file separately. The `successHandler` callback contains `UploadMetadata` with information on the uploaded file and a `byte[]` that contains the actual data from the upload.

```java
InMemoryUploadHandler inMemoryHandler = UploadHandler.inMemory(
    (metadata, data) -> {
        // Get other information about the file.
        String fileName = metadata.fileName();
        String mimeType = metadata.contentType();
        long contentLength = metadata.contentLength();

        // Do something with the file data...
        // processFile(data, fileName);
    });


Upload upload = new Upload(inMemoryHandler);
```

### <a id="fileuploadhandler"></a>FileUploadHandler

The `FileUploadHandler` stores the upload as a file to the file system.

The handler is given a `successHandler` of type `FileUploadCallback successHandler` and a `FileFactory`. This `successHandler` is called for each successful uploaded file separately. The `successHandler` callback contains `UploadMetadata` with information on the uploaded file and the actual `File` where the data was written.

The file used is defined with `FileFactory` that gets the filename for the upload and should return the `File` to store the data into.

```java
FileUploadCallback successHandler = (metadata, file) ->
    System.out.printf("File saved to: %s%n", file.getAbsolutePath());
FileFactory fileFactory = (fileName) -> new File("/path/to/uploads", fileName);
FileUploadHandler fileHandler = UploadHandler.toFile(successHandler, fileFactory);

Upload upload = new Upload(fileHandler);
```

### <a id="temporaryfileuploadhandler"></a>TemporaryFileUploadHandler

The `TemporaryFileUploadHandler` works the same as `FileUploadHandler`, except that instead of taking in a `FileFactory`, it stores the data into a temporary file in the system default temporary-file directory.

The handler is given a `successHandler` of type `FileUploadCallback successHandler`. This `successHandler` is called for each successful uploaded file separately. The `successHandler` callback contains `UploadMetadata` with information on the uploaded file and the actual `File` where the data was written.

```java
FileUploadCallback successHandler = (metadata, file) ->
    System.out.printf("File saved to: %s%n", file.getAbsolutePath());

TemporaryFileUploadHandler temporaryFileHandler = UploadHandler.toTempFile(successHandler);

Upload upload = new Upload(temporaryFileHandler);
```

### <a id="validating-uploads"></a>Validating Uploads with the Built-in Handlers (since V25.3)

The built-in handlers can refuse an upload while it’s being received, without writing a custom `UploadHandler`. Add a validator to a handler with the fluent `withValidator()` method, or one of the phase-specific shortcuts, and call `UploadEvent.reject()` from it to cancel the upload. A rejected upload is never delivered to the success callback, and any partially written file is deleted.

A validator can act in three phases, each running synchronously on the request thread as the upload is received:

- **Metadata** — before any data is read. Use it to reject by file name, client-declared content type, or the client-declared size.

- **Header** — over the first bytes of the content. Use it for file-signature ("magic byte") or MIME-sniffing checks that need only the leading bytes.

- **Complete** — after the whole upload has been received. Use it for whole-content checks, such as antivirus scanning.

Each phase has a fluent shortcut that takes a lambda:

```java
FileUploadHandler handler = UploadHandler
    .toFile(successHandler, fileFactory)
    // Metadata phase: reject before reading any data.
    .validateMetadata(event -> {
        if (!"image/png".equals(event.getContentType())) {
            event.reject("Only PNG images are accepted");
        }
    })
    // Header phase: inspect the first 8 bytes to verify the PNG signature.
    .validateHeader(8, (event, header) -> {
        if (!isPngSignature(header)) {
            event.reject("File is not a valid PNG image");
        }
    })
    // Complete phase: scan the fully received file before it's delivered.
    .validateComplete((event, content) -> {
        content.asPath().ifPresent(path -> {
            if (containsMalware(path)) {
                event.reject("File failed the virus scan");
            }
        });
    });
```

The `validateHeader()` method takes the number of leading bytes to buffer, and the callback receives a read-only `ByteBuffer` view of those bytes (fewer if the upload is smaller). Rejecting during the metadata or header phase stops the upload early, before the rest of the body is read. Rejecting during the complete phase happens only after the whole body has been received, so it can’t be used to limit the upload size.

The `validateComplete()` callback receives an `UploadContent` handle to the received data, offering `getInputStream()`, `size()`, and `asPath()` — the last present only for the file-based handlers. The content is valid only for the duration of the call: don’t retain it or pass its path to a background process, since the underlying file may be deleted as soon as the call returns.

To combine several phases in a single reusable validator, implement `UploadValidator` directly and register it with `withValidator()`. Override `headerSize()` to return a positive value to opt into the header phase; leaving it at the default of `0` skips `validateHeader()` altogether.

```java
public class PngUploadValidator implements UploadValidator {

    @Override
    public void validateMetadata(UploadEvent event) {
        if (!"image/png".equals(event.getContentType())) {
            event.reject("Only PNG images are accepted");
        }
    }

    @Override
    public int headerSize() {
        // Number of leading bytes to buffer for validateHeader().
        return 8;
    }

    @Override
    public void validateHeader(UploadEvent event, ByteBuffer header) {
        if (!isPngSignature(header)) {
            event.reject("File is not a valid PNG image");
        }
    }

    @Override
    public void validateComplete(UploadEvent event, UploadContent content) {
        content.asPath().ifPresent(path -> {
            if (containsMalware(path)) {
                event.reject("File failed the virus scan");
            }
        });
    }
}
```

```java
FileUploadHandler handler = UploadHandler
    .toFile(successHandler, fileFactory)
    .withValidator(new PngUploadValidator());
```

> **Note:** Prefer `reject()` over throwing an exception. In a multipart upload with several files, a rejection lets the remaining files still be processed (the response is HTTP 207), whereas a thrown exception stops the whole request.

When a validator rejects an upload, progress listeners are notified through `onError()` with an `UploadRejectedException` whose message is the rejection reason, so a deliberate rejection can be told apart from a genuine I/O failure.

## <a id="upload-progress-tracking"></a>Upload Progress Tracking

The built-in implementations support `TransferProgressListeners` which can be added through the fluent API directly to the handler for specific events

```java
UploadHandler.toTempFile(
        (metadata, file) -> System.out.printf("File saved to: %s%n",
            file.getAbsolutePath()))
    .whenStart(() -> System.out.println("Upload started"))
    .onProgress((transferredBytes, totalBytes) -> {
        double percentage = (double) transferredBytes / totalBytes * 100;
        System.out.printf("Upload progress: %.2f%%\n", percentage);
    }).whenComplete((success) -> {
        if (success) {
            System.out.println("Upload completed successfully");
        } else {
            System.out.println("Upload failed");
        }
    });
```

or giving a TransferProgressListener through the factory methods as a parameter.

```java
TransferProgressListener progressListener = new TransferProgressListener() {
        @Override
        public void onStart(TransferContext context) {
            Assert.assertEquals(165000, context.contentLength());
            Assert.assertEquals("download", context.fileName());
            invocations.add("onStart");
        }

        @Override
        public void onProgress(TransferContext context,
                long transferredBytes, long totalBytes) {
            double percentage = (double) transferredBytes / totalBytes * 100;
            System.out.printf("Upload progress: %.2f%%\n", percentage);
        }

        @Override
        public void onComplete(TransferContext context,
               long transferredBytes) {
            System.out.println("Upload completed successfully");
        }

        @Override
        public void onError(TransferContext context,
                IOException reason) {
            System.out.println("Upload failed");
        }
    };

UploadHandler.toTempFile(
        (metadata, file) -> System.out.printf("File saved to: %s%n",
            file.getAbsolutePath()), progressListener);
```

To add progress tracking to a custom upload handler, you can extend `TransferProgressAwareHandler`:

```java
public class CustomUploadHandler
        extends TransferProgressAwareHandler<UploadEvent, CustomUploadHandler>
        implements UploadHandler {
    @Override
    public void handleUploadRequest(UploadEvent event) {
        try (InputStream inputStream = event.getInputStream();
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) {
            // Use the TransferUtil.transfer method to copy the data
            // to notify progress listeners
            TransferUtil.transfer(
                    inputStream,
                    outputStream,
                    getTransferContext(event),
                    getListeners());
            // Process the data
            byte[] data = outputStream.toByteArray();
            // ...
        } catch (IOException e) {
            // Notify listeners of the error
            notifyError(event, e);
            throw new UncheckedIOException(e);
        }
    }
    @Override
    protected TransferContext getTransferContext(UploadEvent event) {
        return new TransferContext(
                event.getRequest(),
                event.getResponse(),
                event.getSession(),
                event.getFileName(),
                event.getOwningElement(),
                event.getFileSize());
    }
}
```

With this you can add the fluent methods to add handling for specific progress events.

```java
CustomUploadHandler uploadHandler = new CustomUploadHandler()
    .whenStart(() -> System.out.println("Upload started"))
    .onProgress((transferredBytes, totalBytes) -> {
        double percentage = (double) transferredBytes / totalBytes * 100;
        System.out.printf("Upload progress: %.2f%%\n", percentage);
    })
    .whenComplete((success) -> {
        if (success) {
            System.out.println("Upload completed successfully");
        } else {
            System.out.println("Upload failed");
        }
    });
```

## <a id="handling-upload-requests-in-lit-and-react"></a>Handling Upload Requests in Lit and React

When using the Upload web component standalone, you’ll need an upload file handler or endpoint in your backend to handle the file upload request. By default, the Upload component sends a request with the method type `POST`, the content type `multipart/form-data`, and the request URL (i.e., the current browser location).

Use the `target` attribute to specify a different URL that should handle the upload request. It’s also possible to customize other aspects of the request, such as the method or request headers.

**Lit**

```html
<vaadin-upload
  method="PUT"
  target="/api/upload-handler"
  headers='{ "X-API-KEY": "7f4306cb-bb25-4064-9475-1254c4eff6e5" }'>
</vaadin-upload>
```

**React**

```jsx
<Upload
  method="PUT"
  target="/api/upload-handler"
  headers='{ "X-API-KEY": "7f4306cb-bb25-4064-9475-1254c4eff6e5" }'>
</Upload>
```

`EB618652-4822-49DC-9A51-D71237EF100E`
