Maison >Java >javaDidacticiel >Stockage d'images pour votre application UGC à l'aide de Spring Boot et iDrive E3

Stockage d'images pour votre application UGC à l'aide de Spring Boot et iDrive E3

DDD
DDDoriginal
2024-10-14 06:11:29931parcourir

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.

  • Les fichiers image sont uniquement au format PNG ou JPG.
  • La taille du fichier ne doit pas dépasser 1 Mo.

É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 :

Storing images for your UGC app using Spring Boot and iDrive E3

Schéma de l'application de haut niveau :

Storing images for your UGC app using Spring Boot and iDrive E3

Étape n°3. Construire.

La partie backend nécessitait les éléments suivants pour être complétée :

  • Ajoutez le SDK AWS au projet.
  • Implémentez Image Service pour travailler avec une intégration externe.
  • Implémentez un contrôleur d'image pour placer les images dans un compartiment à l'aide du service d'image et enregistrez l'enregistrement correspondant dans la base de données.

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.

Storing images for your UGC app using Spring Boot and iDrive E3

Ç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!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn