FEAT : Gestion des miniatures via thumbnails

This commit is contained in:
2025-12-03 13:59:26 +01:00
parent bafd5dff1a
commit d26d3742c5
17 changed files with 56 additions and 15 deletions

View File

@@ -87,6 +87,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.20</version>
</dependency>
</dependencies>

View File

@@ -218,4 +218,18 @@ public class PhotoController {
return "redirect:/photo/" + id;
}
/* ========================== THUMBNAIL IMAGE ========================== */
@GetMapping("/photo/{id}/thumb")
public ResponseEntity<Resource> thumb(@PathVariable Long id) {
PhotoDTO photo = photoService.getPhotoById(id);
if (photo == null || photo.getUuidThumbnail() == null)
return ResponseEntity.notFound().build();
Resource r = photoService.loadAsResource(photo.getUuidThumbnail());
if (!r.exists())
return ResponseEntity.notFound().build();
return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).body(r);
}
}

View File

@@ -15,5 +15,6 @@ public class PhotoDTO {
private LocalDateTime dateUpload;
private String visibilite;
private UtilisateurDTO proprietaire;
private String uuidThumbnail;
}

View File

@@ -15,6 +15,7 @@ public class PhotoMapper {
dto.setDateUpload(p.getDateUpload());
dto.setVisibilite(p.getVisibilite().name());
Utilisateur u = p.getProprietaire();
dto.setUuidThumbnail(p.getUuidThumbnail());
if (u != null) {
UtilisateurDTO uDTO = new UtilisateurDTO();
uDTO.setId(u.getId());

View File

@@ -34,4 +34,7 @@ public class Photo {
@OneToMany(mappedBy = "photo", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Partage> partages = new HashSet<>();
@Column
private String uuidThumbnail;
}

View File

@@ -17,7 +17,9 @@ import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.*;
import java.time.LocalDateTime;
@@ -53,14 +55,27 @@ public class PhotoServiceImpl implements PhotoService {
Path uploadPath = Paths.get(uploadDir);
if (!Files.exists(uploadPath))
Files.createDirectories(uploadPath);
Files.copy(file.getInputStream(),
uploadPath.resolve(uuid),
StandardCopyOption.REPLACE_EXISTING);
// ========= LIRE LE FICHIER UNE SEULE FOIS =========
byte[] bytes = file.getBytes();
// ========= SAUVEGARDE ORIGINAL =========
Path originalPath = uploadPath.resolve(uuid);
Files.write(originalPath, bytes);
// ========= MINIATURE =========
String thumbUuid = "thumb-" + uuid + ".jpg";
Path thumbPath = uploadPath.resolve(thumbUuid);
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes)) {
net.coobird.thumbnailator.Thumbnails.of(bis)
.size(300, 300)
.outputFormat("jpg")
.toFile(thumbPath.toFile());
}
// ========= BDD =========
Utilisateur owner = utilisateurRepository.findByEmail(ownerEmail)
.orElseThrow(() -> new RuntimeException("Utilisateur introuvable"));
Photo p = new Photo();
p.setNomFichierOriginal(original);
p.setUuidFichier(uuid);
p.setUuidThumbnail(thumbUuid);
p.setVisibilite(Photo.Visibilite.valueOf(visibilite));
p.setDateUpload(LocalDateTime.now());
p.setProprietaire(owner);
@@ -72,6 +87,8 @@ public class PhotoServiceImpl implements PhotoService {
//============= GET PHOTO BY ID ==========================
@Override
public PhotoDTO getPhotoById(Long id) {

View File

@@ -16,7 +16,7 @@
<h2>Photos dans lalbum</h2>
<div th:each="p : ${album.photos}" style="display:inline-block; margin:10px;">
<a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
<br/>
<a th:if="${canAdmin}" th:href="@{'/album/' + ${album.id} + '/remove/' + ${p.id}}"

View File

@@ -13,7 +13,7 @@
<a th:each="p : ${photosPage.content}"
th:href="@{'/photo/' + ${p.id}}"
style="display:inline-block; margin:10px;">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}"
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}"
width="150"
style="border:1px solid #ccc;"/>
</a>

View File

@@ -12,7 +12,7 @@
<div>
<a th:each="p : ${photosPrivees.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
</div>
<div>
@@ -28,7 +28,7 @@
<div>
<a th:each="p : ${photosPubliques.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
</div>
<div>
@@ -44,7 +44,7 @@
<div style="display:flex; gap:20px; flex-wrap:wrap;">
<div th:each="p : ${mesPhotosPartagees.content}" th:if="${p != null}">
<a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"
style="display:block; border:1px solid #ccc;"/>
</a>
<!--compteur de partages -->
@@ -78,7 +78,7 @@
<a th:each="p : ${photosPartagees.content}"
th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
</div>
<div>

View File

@@ -16,7 +16,7 @@
<h2>Photos dans lalbum</h2>
<div th:each="p : ${album.photos}" style="display:inline-block; margin:10px;">
<a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
<br/>
<a th:if="${canAdmin}" th:href="@{'/album/' + ${album.id} + '/remove/' + ${p.id}}"

View File

@@ -13,7 +13,7 @@
<a th:each="p : ${photosPage.content}"
th:href="@{'/photo/' + ${p.id}}"
style="display:inline-block; margin:10px;">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}"
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}"
width="150"
style="border:1px solid #ccc;"/>
</a>

View File

@@ -12,7 +12,7 @@
<div>
<a th:each="p : ${photosPrivees.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
</div>
<div>
@@ -28,7 +28,7 @@
<div>
<a th:each="p : ${photosPubliques.content}" th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
</div>
<div>
@@ -44,7 +44,7 @@
<div style="display:flex; gap:20px; flex-wrap:wrap;">
<div th:each="p : ${mesPhotosPartagees.content}" th:if="${p != null}">
<a th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"
style="display:block; border:1px solid #ccc;"/>
</a>
<!--compteur de partages -->
@@ -78,7 +78,7 @@
<a th:each="p : ${photosPartagees.content}"
th:if="${p != null}"
th:href="@{'/photo/' + ${p.id}}">
<img th:src="@{'/photo/' + ${p.id} + '/raw'}" width="120"/>
<img th:src="@{'/photo/' + ${p.id} + '/thumb'}" width="120"/>
</a>
</div>
<div>