How to Work with Files
Contents |
Introduction
The main objective of this how to is to show the standard way to handle the file storage in Openbravo. It is the recommended for all developers to make use of the components that are explained in this document because they provide the following advantages:
- Security: they ensure that all files are placed under the expected location, avoiding potential security problems like accessing to restricted files or directories using path traversal.
- A Common API: unify the way of handling files avoiding duplicated and/or obsolete code.
- Cluster Support: support the correct file management being in cluster mode.
Acknowledgments
Before starting make sure that you have read the prerequisite knowledge article.
Type of Files
There are four type of files in Openbravo:
- Attachments: files created as attachments.
- Temporary: files that are used just in the scope of a single request. They are typically deleted once the request finishes.
- Permanent: files that are desired to be kept in the system in long-term.
- Shared Temporary: temporary files that can be accessed by different requests (threads).
In the next sections, we are going to discuss how to handle the storage of each type of file.
Attachments
The attachments should be handled with the AttachImplementationManager. This is a class that centralizes the attachment management. It can be used to upload, delete, update or download files which are attached to a record of a tab according to the attachment method configured in the application.
See here to learn how to create an attachment method.
Temporary Files
![]() | This feature is available starting from 3.0PR20Q1. |
To create temporary files, the TemporaryStorage class should be used. All the files created with this class are placed in the file system default temporary folder.
String name = "test.txt"; try { // Create a temporary file named "test.txt" Path path = TemporaryStorage.getInstance().create(name); ... } finally { // Delete the temporary file Path path = TemporaryStorage.getInstance().delete(name); }
It is allowed to provide an String representing a relative path, and the API will take care of creating also all the nonexistent parent directories.
// Creating a file with parent directory Path path = TemporaryStorage.getInstance().create("test/test.txt");
It is also possible to create a file with a random name:
// Create a file with a random name Path path = TemporaryStorage.getInstance().create();
Or a file with random name but with an specific preffix an suffix:
String prefix = "ob-"; String suffix = ".txt"; // Create a file with a random name with preffix and suffix Path path = TemporaryStorage.getInstance().create(prefix, suffix);
And finally we can retrieve the Path to an already created file:
// Retrieve an existing file. If the file does not exists then the returned Optional will be empty. Optional<Path> optPath = TemporaryStorage.getInstance().get(name)
Permanent Files
![]() | This feature is available starting from 3.0PR20Q1. |
To handle the storage of permanent files an implementation of the StorageHandler interface is required. This interface defines the methods that must be implemented to have a fully functional file storage handler.
Besides, the StorageHandler implementation should be qualified with three annotations:
@ApplicationScoped @Qualifier("SMSearchKey") @PermanentStorage
where:
- ApplicationScoped: indicates that just a single instance (per node if in cluster) of the storage handler is used
- ComponentProvider.Qualifier: is used to identify the configuration in the Storage Configuration window related to the storage handler.
- PermanentStorage: defines that the type of files managed by the storage handler is permanent
As mentioned above, the storage handler to be used for a Client in particular is configured in the Storage Configuration window.
The following fields should be populated at this window:
- Client: the Client affected by the configuration
- Storage Type: Permanent
- Storage Method: the storage method. This is the record of the Storage Method window whose search key is the one used to annotate our storage handler.
Note that if no configuration for permanent files is defined at this window, a default storage handler is used. This storage handler uses the local file system to keep the files. The location of the base (root) directory where the files are placed can be configured by setting the permanent.path property with the absolute path of the base directory in the Openbravo.properties file. If this property is not found, then the value defined in the attach.path property will be used as the default base path.
Once our configuration is ready, we can start working with the storage of permanent files, using the FileStorage class. This class is the entry point when working with this kind of files, allowing to work without taking care about the implementation of the underlying storage method:
String fileName = "file.txt"; // Create an empty permanent file FileStorage.of(StorageType.PERMANENT).create(fileName, null);
// Delete the file FileStorage.of(StorageType.PERMANENT).delete(newName);
InputStream is; // InputStream with the content of the file to be created ... // Create a permanent file with content FileStorage.of(StorageType.PERMANENT).create(fileName, is);
// Get the file content into an InputStream... InputStream content = FileStorage.of(StorageType.PERMANENT).get(fileName); // ...or get is as a Path in the local file system Path path = FileStorage.of(StorageType.PERMANENT).toPath(fileName);
// Move the file to a new location String newName = "moved/file.txt" FileStorage.of(StorageType.PERMANENT).move(fileName, newName);
Besides the FileStorage API provides some utility methods to create a file and return a Path in the local system for it. This is useful when it is needed to immediately refer to the newly created file in order to start working with it:
String fileName = "file.txt"; InputStream is; // InputStream with the content of the file to be created ... // Directly retrieve the path of the newly created file. This is equivalent to do: // 1- StorageHandler.of(StorageType.PERMANENT).create(fileName, is); // 2- return StorageHandler.of(StorageType.PERMANENT).toPath(fileName); Path path = FileStorage.create(StorageType.PERMANENT, fileName, is);
![]() | This feature is available starting from 3.0PR20Q1. |
To handle the storage of shared temporary files the StorageHandler infrastructure is also used.
Similarly, the StorageHandler implementation should be qualified with three annotations:
@ApplicationScoped @Qualifier("SMSearchKey") @SharedTemporaryStorage
And in this case:
- SharedTemporaryStorage: defines that the type of files managed by the storage handler is shared temporary
The configuration in the Storage Configuration window is created as explained above, but using Temporary (Multiple Threads) as Storage Type.
Note that if no configuration is defined for shared temporary files at this window, a default storage handler is used. This storage handler stores the files the local file system, using the default temporary folder as the base directory.
Finally, to make use of the FileStorage API, the type for the shared temporary files should be specified:
// Create a shared temporary file FileStorage.of(StorageType.SHARED_TEMPORARY).create(fileName, inputStream);