File storage backend
CiviForm currently supports file uploads using AWS S3. There is an effort in progress to add support for Azure Blob Storage. Support for other storage services like Google Cloud Storage is planned. This document will provide an overview of the classes and interfaces that are used to implement support for new storage providers.
File upload functionality uses the Azurite / Localstack emulators. For Azure-based file uploads to work in your browser, you will need to add the following line to your local
# Required for test AWS S3 bucket
This is because the application makes requests to Azurite using its container name, which can only be resolved when the request is made from within the Docker network. These container names to be mapped to the loopback IP address in order for the browser to resolve requests to
azuriteto upload and get uploaded images.
Note to Googlers: if you are using your Chrome profile for your Corp account, you'll run into CORS and UberProxy errors. Switch to a personal or incognito Chrome profile.
StorageClientinterface is used to decouple classes for interacting with specific storage providers from the rest of the codebase. New controllers and views should depend on the StorageClient interface rather than one of its implementations. In order to determine which
StorageClientimplementation to use at runtime, we use Guice for dependency injection. The
CloudStorageModuleGuice module reads in the
cloud.storageproperty set in
application.conf, and binds the corresponding implementation to the implementation. For more info on how this works, see the Guice documentation on bindings.
Each implementation of StorageClient uses an implementation of an inner
Clientinterface depending on the environment the application is running in (dev, test, or prod). The inner
Clientimplementations for new classes implementing
StorageClientshould have one client that interacts with the emulator, one stub (for unit tests), and one client that interacts with a real instance of the storage backend.
Class Diagram for StorageClient interface
Implementations of the
StorageUploadRequestclass hold all the information necessary to upload a file from the browser. Implementations of
StorageClientare used to generate instances of
SignedS3UploadRequest) and the generated instances are passed to the render methods in view classes.
Class diagram for StorageUploadRequest interface
The controller and view classes that interact with the interfaces above might need to change the behavior of a method (for example, render a different template) depending on which storage provider is being used. To accomplish this, we make use of the Strategy Pattern. Each class where the strategy pattern is being used has its own corresponding strategy interface. Implementations are bound to the interface in the
CloudStorageModulefile. Below is an example of how the strategy pattern is used:
Strategy pattern example