- Files are always stored internally in ZIP format in order to get CRC-32 check, compression and AES encryption for free.
- This store has a flat structure instead of a hierarchy, so there is no native support for things like directories or folders, however common file name prefixes (like
foo/
,bar/
) can be used to organize objects simulating a hierarchical structure. For more info, please check the Amazon S3 documentation because the semantics are similar. - This library requires JDK 11 and depends on the Spring Framework but no Spring Context is actually needed (see the sample Java code below).
FILENAME | CONTENT_LENGTH | LAST_MODIFIED | COMPRESSED | ENCRYPTED | FILE_CONTENTS | UUID_BASE64URL | CREATION_TIME |
---|---|---|---|---|---|---|---|
foo.txt | 123 | 2022-10-31 23:10:22,607 | 1 | 0 | (BLOB) | IKn6ATU7RVa-qbykef7BfQ |
2022-10-31 23:10:22,610 |
bar.png | 4567 | 2022-10-31 23:10:49,669 | 0 | 0 | (BLOB) | 2WGTuBeQTu-iS5pUccAASQ |
2022-10-31 23:10:49,672 |
baz.zip | 89012 | 2022-10-31 23:11:02,607 | 0 | 1 | (BLOB) | S2LzZ8f5S_6e5fT_p5N0Hw |
2022-10-31 23:11:02,610 |
<dependency>
<groupId>io.github.albertus82.storage</groupId>
<artifactId>relatable-storage</artifactId>
<version>0.2.0</version>
</dependency>
CREATE TABLE storage (
uuid_base64url VARCHAR(22 /* BYTE */) NOT NULL PRIMARY KEY,
filename VARCHAR(1024 /* CHAR */) NOT NULL UNIQUE,
content_length NUMERIC(19, 0) /* NOT NULL DEFERRABLE INITIALLY DEFERRED */ CHECK (content_length >= 0),
last_modified TIMESTAMP NOT NULL,
compressed NUMERIC(1, 0) NOT NULL CHECK (compressed IN (0, 1)),
encrypted NUMERIC(1, 0) NOT NULL CHECK (encrypted IN (0, 1)),
file_contents BLOB NOT NULL,
creation_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
When not using encrypion, you can also enable BLOB deduplication, if supported by your DBMS.
DataSource dataSource = new DriverManagerDataSource("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"); // replace with your connection string or connection pool
StorageOperations storage = new RelaTableStorage(new JdbcTemplate(dataSource), "STORAGE", new FileBufferedBlobExtractor()); // can be customized, see Javadoc
storage.put(new PathResource("path/to/myFile.ext"), "myStoredFile.ext"); // the second argument can be prefixed to simulate a hierarchical structure
Resource resource = storage.get("myStoredFile.ext");
try (InputStream in = resource.getInputStream()) {
byte[] bytes = in.readAllBytes(); // not intended for reading input streams with large amounts of data!
}
See also SampleCodeTest for a runnable JUnit test based on this code.