en_file_uploader 3.1.0
en_file_uploader: ^3.1.0 copied to clipboard
This Dart package provides a file upload functionality that is implementation-agnostic. Provides the capability to upload a file in chunks with built-in retry handling.
File Uploader #
This Dart package provides a file upload functionality that is implementation-agnostic.
Features #
This library provides the capability to:
- ☑ upload a complete file;
- ☑ upload a file in chunks;
- ☑ upload a file in chunks with the ability to pause and resume the upload from where it left off (restorable chunks file upload);
- ☑ apply file transformations (e.g., image compression, format conversion) before uploading via a modular pipeline.
The en_file_uploader package can be used with various libraries for making HTTP requests, such as http, dio, or others. This provides a consistent and straightforward interface for managing file uploads without needing to change your core logic based on the underlying network client used.
This library also supports the web platform.
Upload Handlers (Implementations) #
To use en_file_uploader, you need an upload handler that specifies how the files are sent to your server. Depending on your backend implementation, you can choose from three main types:
FileUploadHandler: For uploading the entire file in a single request.ChunkedFileUploadHandler: For uploading files split into smaller chunks.RestorableChunkedFileUploadHandler: For chunked uploads with the ability to pause and resume (the client asks the server for the current upload progress offset before starting/resuming).
Ready-to-use Plugins #
To avoid writing these handlers from scratch, there are official plugins available that support common HTTP libraries. Check if one of these fits your stack first:
- http_file_uploader: File uploads using the Dart
httplibrary. - dio_file_uploader: File uploads using the
diolibrary.
Custom Handlers #
If the existing plugins do not meet your needs you can build your own custom handler by extending one of the three base classes. See the How to use section below for concrete examples of how to implement them.
UI Integration #
If you are using Flutter, this package integrates seamlessly with flutter_file_uploader, which provides highly customizable widgets for displaying and managing file uploads.
File Uploader APIs #
Support restorable chunked file upload #
To implement a restorable file server, the following functionalities need to be supported:
- An API to present the file; before uploading the chunks, the file is presented and needs an id. This id will be used as a reference for chunk uploads;
- An API that, given the presentation id, allows requesting the file's state. The file's state will return the offset of the next chunk to be sent. This is needed to support retrying from the last unsent chunk.
Configuration #
A global configuration is available to set default values for the entire system.
Currently, it is possible to set a default chunk size.
setDefaultChunkSize(1024)
Logger #
It is possible to implement the FileUploaderLogger class to manage the logs generated by the library.
// It's just an example, not a production-ready version.
// Every log is printed
class PrinterLogger implements FileUploaderLogger {
@override
void error(String message, error, stackTrace) {
print(error);
}
@override
void info(String message) {
print(message);
}
@override
void warning(String message) {
print(message);
}
}
If you're looking for a package to handle logging in your project, you can check out another package I created: en_logger.
File #
File are handled with the XFile class from the cross_file package. This abstraction allow the library to be used across multiple platforms.
File Transformers #
You can apply a pipeline of FileTransformers to a file before it is uploaded. This is useful for tasks like image compression, video transcoding, or adding metadata.
class MyTransformer extends FileTransformer {
@override
Future<XFile> transform(XFile file, {TransformationProgressCallback? onProgress}) async {
// Perform transformation
// Use onProgress?.call(value) to report progress (0.0 to 1.0)
return transformedFile;
}
}
When creating a FileUploadController, you can provide a list of transformers:
final controller = FileUploadController(
handler,
transformers: [MyTransformer()],
);
Transformers are executed in order. The output of one transformer is passed as the input to the next. The final transformed file is then cached and used for the upload (and any subsequent retries).
If you're looking for a package to handle image transformations in your project, you can check out another package I created: image_pipeline.
How to use #
Create a FileUploadController by passing a concrete implementation of FileUploadHandler, ChunkedFileUploadHandler, or RestorableChunkedFileUploadHandler as the handler.
class MyFileUploadHandler extends FileUploadHandler {
MyFileUploadHandler({required super.file});
@override
Future<void> upload({ProgressCallback? onProgress}) {
// TODO: implement upload
// Ex. http.post(url, body: file);
}
}
class MyChunkedFileUploadHandler extends ChunkedFileUploadHandler {
MyChunkedFileUploadHandler({required super.file});
@override
Future<void> uploadChunk(FileChunk chunk, {ProgressCallback? onProgress}) {
// TODO: implement uploadChunk
// Ex. http.post(url, body: chunk);
}
}
class MyRestorableChunkedFileUploadHandler extends RestorableChunkedFileUploadHandler {
MyRestorableChunkedFileUploadHandler({required super.file});
@override
Future<void> uploadChunk(FileChunk chunk, {ProgressCallback? onProgress}) {
// TODO: implement uploadChunk
// Ex. http.post(url, body: chunk);
}
@override
Future<FileUploadPresentationResponse> present() {
// TODO: implement present
// Ex. http.post(url, body: file);
}
@override
Future<FileUploadStatusResponse> status(FileUploadPresentationResponse presentation) {
// TODO: implement status
// Ex. http.get(url, body: presentation);
}
}
The controller will have the capabilities to upload a file and retry the upload.
final handler = getHandler();
final controller = FileUploadController(handler);
controller.upload(); // upload the file
controller.retry(); // retry the upload
controller.uploaded // check if the file is uploaded
Tip #
Implement your own specific handler as an internal, private component and configure your business logic around the FileUploadController. The controller should be the only interface used to manage and display upload state, while keeping the handler implementation hidden.
class MyBusinessLogic extends ChangeNotifier {
MyBusinessLogic({required this.controller});
factory MyBusinessLogic.handler(FileUploadHandler handler) {
return MyBusinessLogic(controller: FileUploadController(handler));
}
final FileUploadController controller;
bool _isUploading = false;
bool get isUploading => _isUploading;
void uploadFile() {
_isUploading = true;
notifyListeners();
controller.upload(onProgress: (progress, total) {
...
});
...
}
}
Example #
In the example, there is an implementation of RestorableChunkedFileUploadHandler handler that sends chunks to a mock server (InMemoryBackend).
Other examples are provided in the tests to ensure the correct functionality of the library.
Running the example #
1. Bootstrap the workspace #
This project uses Melos to manage the monorepo. If you don't have it installed yet:
dart pub global activate melos
Then bootstrap all packages from the repository root:
dart run melos bs
melos bsrunspub getacross all packages and links local dependencies together.
2. Run the Flutter example app #
VS Code (recommended)
The repository ships with a pre-configured launch configuration.
Open the project in VS Code, go to Run and Debug (⇧⌘D), select flutter_file_uploader_example and press ▶ Start Debugging.
Command line
cd flutter_file_uploader/example
flutter run