FEAT : upload en local /opt/photo-app/uploads + visualisation photo

This commit is contained in:
2025-12-01 21:00:43 +01:00
parent b4889e7ec9
commit e2c714185b
12 changed files with 98 additions and 11 deletions

View File

@@ -2,6 +2,7 @@ package local.epul4a.fotosharing.controller;
import local.epul4a.fotosharing.model.Photo; import local.epul4a.fotosharing.model.Photo;
import local.epul4a.fotosharing.model.Utilisateur; import local.epul4a.fotosharing.model.Utilisateur;
import local.epul4a.fotosharing.repository.PhotoRepository;
import local.epul4a.fotosharing.security.CustomUserDetails; import local.epul4a.fotosharing.security.CustomUserDetails;
import local.epul4a.fotosharing.service.PhotoService; import local.epul4a.fotosharing.service.PhotoService;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
@@ -16,15 +17,18 @@ import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@Controller @Controller
public class PhotoController { public class PhotoController {
private final PhotoService photoService; private final PhotoService photoService;
private final PhotoRepository photoRepository;
public PhotoController(PhotoService photoService) { public PhotoController(PhotoService photoService, PhotoRepository photoRepository) {
this.photoService = photoService; this.photoService = photoService;
this.photoRepository = photoRepository;
} }
@GetMapping("/upload") @GetMapping("/upload")
@@ -35,12 +39,13 @@ public class PhotoController {
@PostMapping("/upload") @PostMapping("/upload")
public String doUpload(@RequestParam("file") MultipartFile file, public String doUpload(@RequestParam("file") MultipartFile file,
@RequestParam(value="visibilite", defaultValue = "PRIVATE") String visibilite, @RequestParam(value="visibilite", defaultValue = "PRIVATE") String visibilite,
@AuthenticationPrincipal CustomUserDetails user, Authentication authentication,
Model model) { Model model) {
try { try {
Photo p = photoService.store(file, visibilite, user.getUsername()); String email = authentication.getName(); // l'email de l'utilisateur connecté
Photo p = photoService.store(file, visibilite, email);
model.addAttribute("message", "Upload OK : " + p.getId()); model.addAttribute("message", "Upload OK : " + p.getId());
return "redirect:/"; // ou page d'affichage return "redirect:/";
} catch (Exception e) { } catch (Exception e) {
model.addAttribute("error", e.getMessage()); model.addAttribute("error", e.getMessage());
return "upload"; return "upload";
@@ -48,22 +53,38 @@ public class PhotoController {
} }
@GetMapping("/photo/{id}/raw") @GetMapping("/photo/{id}/raw")
public ResponseEntity<Resource> rawPhoto(@PathVariable("id") String idOrUuid) { public ResponseEntity<Resource> rawPhoto(@PathVariable("id") Long id) {
// idOrUuid peut être uuid stocké ou id numeric ; ici on assume uuid Photo photo = photoRepository.findById(id).orElse(null);
Path p = photoService.loadAsPath(idOrUuid); if (photo == null) {
return ResponseEntity.notFound().build();
}
Path p = photoService.loadAsPath(photo.getUuidFichier());
Resource r = new PathResource(p); Resource r = new PathResource(p);
if (!r.exists()) { if (!r.exists()) {
return ResponseEntity.notFound().build(); return ResponseEntity.notFound().build();
} }
String contentType = "application/octet-stream"; String contentType = "application/octet-stream";
try { try {
// tentative de détection basique contentType = Files.probeContentType(p);
contentType = java.nio.file.Files.probeContentType(p);
} catch (Exception ignored) {} } catch (Exception ignored) {}
return ResponseEntity.ok() return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType != null ? contentType : "application/octet-stream")) .contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + p.getFileName().toString() + "\"") .header(HttpHeaders.CONTENT_DISPOSITION,
"inline; filename=\"" + photo.getNomFichierOriginal() + "\"")
.body(r); .body(r);
} }
@GetMapping("/mes-photos")
public String mesPhotos(Model model, Authentication authentication) {
String email = authentication.getName();
model.addAttribute("photos", photoService.listByOwner(email));
return "mes-photos";
}
} }

View File

@@ -2,6 +2,9 @@ package local.epul4a.fotosharing.repository;
import local.epul4a.fotosharing.model.Photo; import local.epul4a.fotosharing.model.Photo;
import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PhotoRepository extends JpaRepository<Photo, Long> { public interface PhotoRepository extends JpaRepository<Photo, Long> {
List<Photo> findByProprietaire_Email(String email);
} }

View File

@@ -4,8 +4,11 @@ import local.epul4a.fotosharing.model.Photo;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
public interface PhotoService { public interface PhotoService {
Photo store(MultipartFile file, String visibilite, String ownerEmail) throws IOException; Photo store(MultipartFile file, String visibilite, String ownerEmail) throws IOException;
Path loadAsPath(String uuidFile); Path loadAsPath(String uuidFile);
List<Photo> listByOwner(String email);
} }

View File

@@ -13,6 +13,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException; import java.io.IOException;
import java.nio.file.*; import java.nio.file.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID; import java.util.UUID;
@Service @Service
@@ -58,4 +59,9 @@ public class PhotoServiceImpl implements PhotoService {
public Path loadAsPath(String uuidFile) { public Path loadAsPath(String uuidFile) {
return Paths.get(uploadDir).resolve(uuidFile); return Paths.get(uploadDir).resolve(uuidFile);
} }
@Override
public List<Photo> listByOwner(String email) {
return photoRepository.findByProprietaire_Email(email);
}
} }

View File

@@ -6,6 +6,7 @@
<body> <body>
<h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1> <h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1>
<p><a th:href="@{/upload}">Uploader une photo</a></p> <p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/mes-photos}">Voir mes photos</a></p>
<p> <p>
<form th:action="@{/logout}" method="post" style="display: inline;"> <form th:action="@{/logout}" method="post" style="display: inline;">
<button type="submit" <button type="submit"

View File

@@ -0,0 +1,26 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Mes photos</title>
</head>
<body>
<h1>Mes photos</h1>
<p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/}">Retour accueil</a></p>
<div th:if="${#lists.isEmpty(photos)}">
<p>Vous n'avez pas encore de photos.</p>
</div>
<ul th:if="${!#lists.isEmpty(photos)}">
<li th:each="p : ${photos}">
<span th:text="${p.nomFichierOriginal}">Nom du fichier</span>
<a th:href="@{'/photo/' + ${p.id} + '/raw'}" target="_blank">Voir</a>
</li>
</ul>
</body>
</html>

View File

@@ -6,6 +6,7 @@
<body> <body>
<h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1> <h1>Bienvenue sur FotoSharing<span th:if="${prenom}" th:text="' : ' + ${prenom}"></span></h1>
<p><a th:href="@{/upload}">Uploader une photo</a></p> <p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/mes-photos}">Voir mes photos</a></p>
<p> <p>
<form th:action="@{/logout}" method="post" style="display: inline;"> <form th:action="@{/logout}" method="post" style="display: inline;">
<button type="submit" <button type="submit"

View File

@@ -0,0 +1,26 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8"/>
<title>Mes photos</title>
</head>
<body>
<h1>Mes photos</h1>
<p><a th:href="@{/upload}">Uploader une photo</a></p>
<p><a th:href="@{/}">Retour accueil</a></p>
<div th:if="${#lists.isEmpty(photos)}">
<p>Vous n'avez pas encore de photos.</p>
</div>
<ul th:if="${!#lists.isEmpty(photos)}">
<li th:each="p : ${photos}">
<span th:text="${p.nomFichierOriginal}">Nom du fichier</span>
<a th:href="@{'/photo/' + ${p.id} + '/raw'}" target="_blank">Voir</a>
</li>
</ul>
</body>
</html>