headers);
+
+ /**
+ * Generate a token for the given entity file. This token can be used to grant temporary access to the file without requiring authentication.
+ *
+ * @param entityFile entity file
+ * @return token
+ */
+ String generateToken(EntityFile entityFile);
+
+
+}
diff --git a/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/StoredEntityFile.java b/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/StoredEntityFile.java
index 5040a075..79667dac 100644
--- a/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/StoredEntityFile.java
+++ b/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/StoredEntityFile.java
@@ -20,41 +20,66 @@
import java.io.File;
import java.io.Serializable;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import tools.dynamia.io.VirtualFile;
import tools.dynamia.modules.entityfile.domain.EntityFile;
public abstract class StoredEntityFile implements Serializable {
- /**
- *
- */
- private static final long serialVersionUID = 421213041955145817L;
- private EntityFile entityFile;
- private String url;
- private File realFile;
+ /**
+ *
+ */
+ private static final long serialVersionUID = 421213041955145817L;
+ private EntityFile entityFile;
+ private String url;
+ private File realFile;
- public StoredEntityFile(EntityFile entityFile, String url, File realFile) {
- super();
- this.entityFile = entityFile;
- this.url = url;
- this.realFile = realFile;
- }
+ public StoredEntityFile(EntityFile entityFile, String url, File realFile) {
+ super();
+ this.entityFile = entityFile;
+ this.url = url;
+ this.realFile = realFile;
+ }
- public EntityFile getEntityFile() {
- return entityFile;
- }
+ public EntityFile getEntityFile() {
+ return entityFile;
+ }
- public String getUrl() {
- return url;
- }
+ public String getUrl() {
+ return url;
+ }
- public String getThumbnailUrl() {
- return getThumbnailUrl(200, 200);
- }
+ public String getThumbnailUrl() {
+ return getThumbnailUrl(200, 200);
+ }
- public abstract String getThumbnailUrl(int width, int height);
+ public abstract String getThumbnailUrl(int width, int height);
- public File getRealFile() {
- return realFile;
- }
+ public File getRealFile() {
+ return realFile;
+ }
+ public File getThumbnailFile(int width, int height) {
+ return realFile;
+ }
+
+ public Resource toResource() {
+ if (realFile != null) {
+ if (realFile.exists() && realFile.isFile()) {
+ return new FileSystemResource(realFile);
+ }
+ }
+ return null;
+ }
+
+ public Resource toThumbnailResource(int width, int height) {
+ File thumbnailFile = getThumbnailFile(width, height);
+ if (thumbnailFile != null) {
+ if (thumbnailFile.exists() && thumbnailFile.isFile()) {
+ return new FileSystemResource(thumbnailFile);
+ }
+ }
+ return null;
+ }
}
diff --git a/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/UploadedFileInfo.java b/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/UploadedFileInfo.java
index fd5d101c..41517872 100644
--- a/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/UploadedFileInfo.java
+++ b/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/UploadedFileInfo.java
@@ -55,6 +55,11 @@ public UploadedFileInfo(FileInfo info) {
this.fullName = info.getName();
this.length = info.getFile().length();
this.source = info.getFile();
+ try {
+ this.contentType = Files.probeContentType(info.getFile().toPath());
+ } catch (IOException e) {
+
+ }
}
public UploadedFileInfo(Path path) {
@@ -83,6 +88,10 @@ public UploadedFileInfo(String fullName, String contentType, InputStream inputSt
this.source = inputStream;
}
+ public void detectContentType() {
+
+ }
+
public String getStoredFileName() {
return storedFileName;
}
diff --git a/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/controller/EntityFileStorageController.java b/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/controller/EntityFileStorageController.java
new file mode 100644
index 00000000..cd32e128
--- /dev/null
+++ b/extensions/entity-files/sources/core/src/main/java/tools/dynamia/modules/entityfile/controller/EntityFileStorageController.java
@@ -0,0 +1,790 @@
+package tools.dynamia.modules.entityfile.controller;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.jspecify.annotations.NonNull;
+import org.springframework.core.io.Resource;
+import org.springframework.http.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
+import tools.dynamia.commons.MapBuilder;
+import tools.dynamia.domain.services.CrudService;
+import tools.dynamia.domain.util.DomainUtils;
+import tools.dynamia.integration.Containers;
+import tools.dynamia.integration.sterotypes.Controller;
+import tools.dynamia.modules.entityfile.UploadedFileInfo;
+import tools.dynamia.modules.entityfile.EntityFileAccountProvider;
+import tools.dynamia.modules.entityfile.EntityFileSecurityProvider;
+import tools.dynamia.modules.entityfile.domain.EntityFile;
+import tools.dynamia.modules.entityfile.enums.EntityFileType;
+import tools.dynamia.modules.entityfile.service.EntityFileService;
+import tools.dynamia.web.util.HttpUtils;
+
+import java.io.ByteArrayInputStream;
+import java.io.Serializable;
+import java.util.Base64;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+import static tools.dynamia.modules.entityfile.local.LocalEntityFileStorageHandler.getParam;
+import static tools.dynamia.modules.entityfile.local.LocalEntityFileStorageHandler.isThumbnail;
+
+/**
+ * Web controller responsible for exporting, downloading and uploading {@link EntityFile} resources.
+ *
+ * In addition to read/download operations, this controller exposes upload endpoints for both
+ * {@code multipart/form-data} and JSON payloads containing Base64 file content. Upload requests may
+ * optionally be associated with an existing domain entity using its fully qualified class name and ID.
+ */
+@Controller
+public class EntityFileStorageController {
+
+ private final EntityFileService entityFileService;
+ private final CrudService crudService;
+
+ /**
+ * Creates a new controller instance.
+ *
+ * @param entityFileService service used to create, resolve and download entity files
+ * @param crudService service used to resolve target entities dynamically by class name and ID
+ */
+ public EntityFileStorageController(EntityFileService entityFileService, CrudService crudService) {
+ this.entityFileService = entityFileService;
+ this.crudService = crudService;
+ }
+
+ /**
+ * Exports the main metadata of a stored file, including a public or tokenized URL when applicable.
+ *
+ * @param uuid unique file identifier
+ * @param response current HTTP response
+ * @param request current HTTP request
+ * @return a JSON payload with basic file metadata or {@code 404} when the file cannot be resolved
+ */
+ @GetMapping(value = "/api/storage/{uuid}/export", produces = MediaType.APPLICATION_JSON_VALUE)
+ public ResponseEntity