Maison >Java >javaDidacticiel >Stockage d'images pour votre application UGC à l'aide de Spring Boot et iDrive E3
Bonjour tout le monde !
Ceci est le deuxième article sur mon expérience chez Makers dans la création d'un clone de réseau social. Lien vers le premier article - https://dev.to/olnov/working-with-http-requests-in-spring-boot-5el0. L'actuel est dédié au stockage d'images pour les profils des utilisateurs.
Étape n°1. Planification.
Dans le cadre de notre MVP, nous avons décidé de mettre en œuvre la user story suivante :
En tant qu'utilisateur,
Je souhaite mettre à jour mon profil pour inclure ma photo,
Pour que les autres utilisateurs puissent me trouver facilement dans l'application
Nous nous sommes également mis d'accord sur des exigences non fonctionnelles.
Étape n°2. Analyser et concevoir.
Nous avons proposé différentes options pour stocker les images : dossier de serveur distant, base de données et stockage d'objets. Même si notre application n'était pas très chargée, nous avons décidé d'utiliser S3 comme solution la plus appropriée pour les systèmes distribués. (P.S. et j'avais un abonnement iDrive e2 existant pour un projet précédent).
Dans la base de données, il n'y avait que des liens vers l'URL publique du stockage iDrive.
Voici le schéma DB :
Schéma de l'application de haut niveau :
Étape n°3. Construire.
La partie backend nécessitait les éléments suivants pour être complétée :
Dans pom.xml, j'ai ajouté ce qui suit :
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.28.18</version> </dependency>
Classe ImageService :
package com.makersacademy.acebook.service; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectResponse; import java.io.InputStream; import java.net.URI; import java.util.UUID; @Service public class ImageService { private final S3Client s3Client; @Value("${idrive.e2.bucket-name}") private String bucketName; @Value("${idrive.e2.endpoint}") private String endpoint; @Value("${idrive.e2.public_endpoint}") private String public_endpoint; public ImageService(@Value("${idrive.e2.access-key}") String accessKey, @Value("${idrive.e2.secret-key}") String secretKey, @Value("${idrive.e2.endpoint}") String endpoint) { AwsBasicCredentials credentials = AwsBasicCredentials.create(accessKey, secretKey); this.s3Client = S3Client.builder() .credentialsProvider(StaticCredentialsProvider.create(credentials)) .endpointOverride(URI.create(endpoint)) .region(Region.AWS_GLOBAL) // I dind't see exact instructions from iDrive, so I set this parameter to AWS_GLOBAL .build(); } public String uploadImage(InputStream imageStream, long contentLength, String contentType) { String key = "media/" + UUID.randomUUID(); PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(key) .contentType(contentType) .contentLength(contentLength) // Specify the content length .build(); s3Client.putObject(putObjectRequest, software.amazon.awssdk.core.sync.RequestBody.fromInputStream(imageStream, contentLength)); // Return the image URL return public_endpoint + "/" + bucketName + "/" + key; } }
Les variables de connexion Idrive sont stockées dans le fichier application.properties :
#S3 storage settings idrive.e2.access-key=${E2_ACCESS_KEY} idrive.e2.secret-key=${E2_SECRET_KEY} idrive.e2.endpoint=${E2_ENDPOINT} idrive.e2.bucket-name=${E2_BUCKET_NAME} idrive.e2.public_endpoint=${E2_PUBLIC_ENDPOINT}
Ok, maintenant Classe ImageController :
package com.makersacademy.acebook.controller; import com.makersacademy.acebook.model.Post; import com.makersacademy.acebook.repository.PostRepository; import com.makersacademy.acebook.service.ImageService; import com.makersacademy.acebook.model.User; import com.makersacademy.acebook.repository.UserRepository; import jakarta.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.view.RedirectView; import java.io.IOException; @Controller public class ImageController { @Autowired private ImageService imageService; @Autowired private UserRepository userRepository; @Autowired private PostRepository postRepository; @PostMapping("/upload/{userId}") public RedirectView uploadImage(@PathVariable Long userId, @RequestParam("file") MultipartFile file) { try { // Check if the user exists User user = userRepository.findById(userId).orElseThrow(()->new RuntimeException("User not found")); // Upload image to IDrive e2 and get image URL String imageUrl = imageService.uploadImage(file.getInputStream(), file.getSize(), file.getContentType()); // Save the image URL in the user's profile user.setUser_photo(imageUrl); userRepository.save(user); return new RedirectView("/media/"+userId); } catch (IOException e) { throw new RuntimeException("Error uploading file:"+e.getMessage()); } } @PostMapping("/upload/post") public RedirectView uploadImageAsPost(@RequestParam("file") MultipartFile file, HttpSession session){ try { Long userId = (Long) session.getAttribute("user_id"); if (userId == null) { throw new RuntimeException("Not authorized"); } String imageUrl = imageService.uploadImage(file.getInputStream(), file.getSize(), file.getContentType()); User user = userRepository.findById(userId) .orElseThrow(() -> new RuntimeException("User not found")); Post post = new Post(imageUrl,user,true); postRepository.save(post); return new RedirectView("/"); } catch (IOException e) { throw new RuntimeException("Error uploading file:"+e.getMessage()); } } }
Sur le front-end pour télécharger l'image de profil, j'ai ajouté ce qui suit :
<div class="mb-3"> <form id="image-form" th:action="@{/api/images/upload/{id}(id=${user.id})}" method="post" enctype="multipart/form-data"> <"formFile" class="form-label btn btn-primary">Upload Image</label> <input class="form-control" type="file" name="file" id="formFile" accept=".jpg,.jpeg,.png" onchange="autoSubmitImage()" hidden/> </form> </div> <script th:inline="javascript"> /*<![CDATA[*/ const autoSubmitImage = ()=> { document.getElementById("image-form").submit(); } /*]]>*/ </script>
Et le résultat.
Ça y est.
Bravo !
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!