Lädt...


🔧 Storing images for your UGC app using Spring Boot and iDrive E2 S3


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

Hello world!

This is the second article about my experience at Makers building a social network clone. Link to the first article - https://dev.to/olnov/working-with-http-requests-in-spring-boot-5el0. The current one is dedicated to storing images for users' profiles.

Step #1. Planning.

As a part of our MVP, we decided to implement the following user story:

As a user,

I want to update my profile to include my photo,

So that other users can easily find me in the app

We also agreed on non-functional requirements.

  • Image files are PNG or JPG only.
  • File size should not exceed 1 MB.

Step #2. Analyze and Design.

We came up with different options for where to store images - remote server folder, database and object storage. Even though our application was not highly loaded, we decided to use S3 as the most appropriate solution for distributed systems. (P.S. and I had an existing iDrive e2 subscription from a previous project).

In the Database, there were only links to the public URL of iDrive storage.

Here is the DB diagram:

Image description

High-level app diagram:

Image description

Step #3. Build.

The backend part needed the following to be completed:

  • Add AWS SDK to the project.
  • Implement Image Service to work with external integration.
  • Implement an Image Controller to put images into a bucket using the Image Service and save the corresponding record in the DB.

In pom.xml I added the following:

       <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>s3</artifactId>
            <version>2.28.18</version>
        </dependency>

ImageService Class:

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;
    }
}

Idrive connection variables are stored in application.properties file:

#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, now ImageController Class:

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());
        }
    }
}

On the front end to upload the profile image I added the following:

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

And the result.

Image description

This is it.

Cheers!

...

🔧 Storing images for your UGC app using Spring Boot and iDrive E2 S3


📈 101.31 Punkte
🔧 Programmierung

🔧 The Power of UGC Images in Enhancing Brand Engagement


📈 34.65 Punkte
🔧 Programmierung

📰 software-architektur.tv: GraalVM mit Spring Native, Spring Boot und Spring Cloud


📈 34.1 Punkte
📰 IT Nachrichten

🔧 Spring vs Spring MVC vs Spring Boot: A Detailed Comparison for Java Developers


📈 34.1 Punkte
🔧 Programmierung

🔧 How to Use Spring Boot Eureka Server in Spring Boot 3.3.0+


📈 32.74 Punkte
🔧 Programmierung

🎥 Microsoft Create: Create a UGC portfolio using PowerPoint


📈 32.65 Punkte
🎥 Video | Youtube

🔧 Storing JSON in PostgreSQL: A Guide to Modern Data Management (With Example in spring boot)


📈 32.18 Punkte
🔧 Programmierung

🔧 Build CRUD RESTful API Using Spring Boot 3, Spring Data JPA, Hibernate, and MySQL Database


📈 30.61 Punkte
🔧 Programmierung

🔧 Implementing Token-Based Authentication in Spring Boot Using Spring Security, JWT, and JDBC Template


📈 30.61 Punkte
🔧 Programmierung

🕵️ http://www.udl-ugc.gov.bd/admin/


📈 28.58 Punkte
🕵️ Hacking

🕵️ http://ugc.portal.gov.bd/dead.php


📈 28.58 Punkte
🕵️ Hacking

🔧 Google Helpful Content Update: Schlechter UGC kann zu Abwertung führen


📈 28.58 Punkte
🔧 Programmierung

🔧 Locking Down Your Spring Boot Apps: A Deep Dive into Spring Security


📈 28.05 Punkte
🔧 Programmierung

🔧 How to Use Spring Profiles in Your Spring Boot Application


📈 28.05 Punkte
🔧 Programmierung

🔧 Securing Your Spring Boot Application with Spring Security


📈 28.05 Punkte
🔧 Programmierung

🔧 Master REST API Development and Streamline Data Access with Spring Boot and Spring Data JPA


📈 27.85 Punkte
🔧 Programmierung

📰 IDrive Online Backup releases EPYC, a secure video conferencing and AR powered video sharing app


📈 27.6 Punkte
📰 IT Security Nachrichten

🔧 Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl


📈 26.54 Punkte
🔧 Programmierung

🔧 Learn Spring Boot and Spring Data JPA


📈 26.54 Punkte
🔧 Programmierung

🔧 Generative AI With Spring Boot and Spring AI


📈 26.54 Punkte
🔧 Programmierung

🔧 Integration Testing With Keycloak, Spring Security, Spring Boot, and Spock Framework


📈 26.54 Punkte
🔧 Programmierung

🔧 Build a Shopping Cart Backend with Spring Boot and Spring Security


📈 26.54 Punkte
🔧 Programmierung

🔧 Master Spring Boot and Spring Security: Build a Shopping Cart Backend


📈 26.54 Punkte
🔧 Programmierung

🔧 Developing Microservices with Spring Boot and Spring Cloud


📈 26.54 Punkte
🔧 Programmierung

🔧 Storing and Publishing your Docker Images


📈 26 Punkte
🔧 Programmierung

🔧 Spring Boot Native Images: Supercharge Your Apps in 15 Words


📈 25.25 Punkte
🔧 Programmierung

📰 Spring Boot 2.0 mit Support für Spring Framework 5.0 veröffentlicht


📈 25.24 Punkte
📰 IT Nachrichten

🔧 Introducing Azure Spring Cloud: fully managed service for Spring Boot microservices


📈 25.24 Punkte
🔧 Programmierung

🔧 Introducing Azure Spring Cloud: fully managed service for Spring Boot microservices


📈 25.24 Punkte
🔧 Programmierung

📰 Spring Tools 4.6.0 versprechen mehr Leistung für Spring Boot


📈 25.24 Punkte
📰 IT Nachrichten

matomo