import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import org.sitenetsoft.quarkus.tus.runtime.event.*;
@ApplicationScoped
public class UploadProcessor {
void onCompleted(@Observes TusUploadCompletedEvent event) {
// Trigger virus scan
scanService.scanAsync(event.uploadId());
}
void onTerminated(@Observes TusUploadTerminatedEvent event) {
// Clean up any related database records
uploadRepository.deleteByUploadId(event.uploadId());
}
}
Event Types
All events are Java records in the org.sitenetsoft.quarkus.tus.runtime.event package.
TusUploadCreatedEvent
Fired when a new upload is created via POST.
| Field | Type | Description |
|---|---|---|
|
|
UUID of the created upload |
|
|
Declared upload size in bytes (-1 if deferred) |
|
|
Whether the upload size is deferred |
|
|
Whether this is a partial upload (for concatenation) |
|
|
Raw |
TusChunkReceivedEvent
Fired after each successful PATCH writes a chunk of data.
| Field | Type | Description |
|---|---|---|
|
|
UUID of the upload |
|
|
Size of the received chunk in bytes |
|
|
New upload offset after writing the chunk |
|
|
Declared total upload size in bytes |
TusUploadCompletedEvent
Fired when an upload is fully received (offset equals total size).
Fired at most once per upload. The event is latched on the upload’s state and the latch is persisted, so re-sending a PATCH at the final offset — or restarting the application — does not fire it again. Observers that move files, insert rows, call webhooks or bill for an upload can rely on this.
| Field | Type | Description |
|---|---|---|
|
|
UUID of the completed upload |
|
|
Total upload size in bytes |
|
|
Raw |
|
|
Authenticated user ID who created the upload, or null |
TusUploadTerminatedEvent
Fired when an upload is deleted via DELETE.
| Field | Type | Description |
|---|---|---|
|
|
UUID of the terminated upload |
TusConcatenationCompletedEvent
Fired when a final concatenation merges partial uploads.
| Field | Type | Description |
|---|---|---|
|
|
UUID of the merged final upload |
|
|
UUIDs of the partial uploads that were merged |
|
|
Total size of the merged upload in bytes |
|
|
Raw |
|
|
Authenticated user ID, or null |
Observing Events
|
Note
|
Events are fired synchronously by default. A synchronous observer blocks the HTTP response until it returns. Keep observer logic fast, or use @ObservesAsync for long-running processing (see below).
|
Use standard CDI @Observes to handle events:
Asynchronous Observation
For long-running processing, use @ObservesAsync to avoid blocking the HTTP response:
import jakarta.enterprise.event.ObservesAsync;
@ApplicationScoped
public class AsyncUploadProcessor {
void onCompleted(@ObservesAsync TusUploadCompletedEvent event) {
// This runs in a separate thread, doesn't block the HTTP response
thumbnailService.generate(event.uploadId());
}
}
|
Note
|
When using @ObservesAsync, the event must be fired with event.fireAsync(). The extension fires events synchronously by default. To use async observation, you can create a synchronous observer that re-fires the event asynchronously.
|
Parsing Upload Metadata
The metadata field in events contains the raw Upload-Metadata header value. TUS metadata is formatted as comma-separated key-value pairs where values are Base64-encoded:
filename dGVzdC50eHQ=,filetype dGV4dC9wbGFpbg==
Use TusUtils.parseMetadata() to decode it:
import org.sitenetsoft.quarkus.tus.runtime.TusUtils;
import java.util.Map;
void onCreated(@Observes TusUploadCreatedEvent event) {
Map<String, String> metadata = TusUtils.parseMetadata(event.metadata());
String filename = metadata.get("filename"); // "test.txt"
String filetype = metadata.get("filetype"); // "text/plain"
}