Spring Boot と iDrive E3 を使用して UGC アプリの画像を保存する

DDD
リリース: 2024-10-14 06:11:29
オリジナル
897 人が閲覧しました

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:

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

High-level app diagram:

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

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.

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

This is it.

Cheers!

以上がSpring Boot と iDrive E3 を使用して UGC アプリの画像を保存するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート