0

I have a product controller and my goal is to receive an upload image, save the url in my database and send the image to amazonS3. In my settings I have the S3Client configuration like this:

package com.api.business_manager_api.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

@Configuration
public class AmazonConfig {
    private final String AWS_REGION = "us-east-1";
    private final String BUCKET_NAME = "stockproducts";

    @Bean
    public S3Client s3Client() {
        return S3Client.builder()
                .region(Region.of(AWS_REGION))
                .credentialsProvider(EnvironmentVariableCredentialsProvider.create())
                .build();
    }
}

So I get it in my controller like this

package com.api.business_manager_api.Controllers;

import com.api.business_manager_api.Dtos.ProductDto;
import com.api.business_manager_api.Mappers.ProductMapper;
import com.api.business_manager_api.Models.CategoryModel;
import com.api.business_manager_api.Models.ProductModel;
import com.api.business_manager_api.Services.CategoryService;
import com.api.business_manager_api.Services.ProductService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

@RestController
@CrossOrigin(origins = "*", maxAge = 3600)
@RequestMapping("/product")
public class ProductController {
    final ProductService productService;
    final CategoryService categoryService;
    final ProductMapper productMapper;
    final S3Client s3Client;

    private final String BUCKET_NAME = "stockproducts" + System.currentTimeMillis();
    private final String KEY = "*my secret key*";

    public ProductController(ProductService productService, ProductMapper productMapper, CategoryService categoryService, S3Client s3Client) {
        this.productService = productService;
        this.productMapper = productMapper;
        this.categoryService = categoryService;
        this.s3Client = s3Client;
    }
    @PostMapping
    public ResponseEntity<Object> saveProduct (@RequestBody @Valid ProductDto productDto, @RequestParam("file")MultipartFile file) {
        try {
            if (productService.existsByProduct(productDto.getProduct())) {
                return ResponseEntity.status(HttpStatus.CONFLICT).body("Product already exists!");
            }
            ProductModel productModel = productMapper.toProductModel(productDto);
            CategoryModel categoryModel = categoryService.findById(productDto.getProductCategory().getCategory_id())
                    .orElseThrow(() -> new RuntimeException("Category not found"));
            productModel.setProductCategory(categoryModel);

            s3Client.putObject(PutObjectRequest
                    .builder()
                    .bucket(BUCKET_NAME)
                    .key(KEY)
                    .build(),
                    RequestBody.fromString("Testing with the {sdk-java}"));

            return ResponseEntity.status(HttpStatus.CREATED).body(productService.save(productModel));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body("Cannot create product. Check if the fields sent in your request are correct.");
        }
    }

    @GetMapping
    public ResponseEntity<List<ProductModel>> getAllProducts() {
        return ResponseEntity.status(HttpStatus.OK).body(productService.findAll());
    }

    @GetMapping("/{id}")
    public ResponseEntity<Object> getOneProduct(@PathVariable(value = "id")UUID id) {
        Optional<ProductModel> productModelOptional = productService.findById(id);
        if (!productModelOptional.isPresent()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Product not found");
        }
        return ResponseEntity.status(HttpStatus.OK).body(productModelOptional.get());
    }

    @PutMapping("/{id}")
    public ResponseEntity<Object> updateProduct(@PathVariable(value = "id") UUID id,
                                                @RequestBody @Valid ProductDto productDto) {
        Optional<ProductModel> productModelOptional = productService.findById(id);
        if (!productModelOptional.isPresent()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Product not found!");
        }
        ProductModel productModel = productModelOptional.get();
        productModel.setProduct(productDto.getProduct());
        productModel.setDescription(productDto.getDescription());
        productModel.setPrice(productDto.getPrice());
        productModel.setExtraPrice(productDto.getExtraPrice());
        productModel.setStock(productDto.getStock());
        productModel.setProductCategory(productDto.getProductCategory());

        return ResponseEntity.status(HttpStatus.OK).body(productService.save(productModel));
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Object> deleteProduct(@PathVariable(value = "id") UUID id) {
        Optional<ProductModel> productModelOptional = productService.findById(id);
        if (!productModelOptional.isPresent()) {
            return ResponseEntity.status(HttpStatus.NOT_FOUND).body("Product not found!");
        }
        productService.delete(productModelOptional.get());
        return ResponseEntity.status(HttpStatus.OK).body("Product deleted succesfully!");
    }
}

The problem is that when sending through s3Client exactly the way the documentation says, I have a syntax error

Cannot resolve method 'fromString' in 'RequestBody'

I already tried to change it to StringOf, but without success. The documentation doesn't say anything about this. Another doubt I have, "key" would be my correct IAM user key? Because the documentation doesn't specify that either

Routfin
  • 397
  • 1
  • 11
  • Can it happen that you use IntelliJ IDEA? – tevemadar Mar 02 '23 at 18:29
  • Yes. I am using Intellij IDEA – Routfin Mar 02 '23 at 18:38
  • According to this older topic you may simply need to reconfigure it: https://stackoverflow.com/questions/4618794/intellij-idea-cannot-resolve-symbol-and-cannot-resolve-method (while the question is Mac, answers and links mention encountering the problem on Windows and Linux too) – tevemadar Mar 02 '23 at 18:43
  • I'll read about it... About the key issue. Can you tell me if the key that must be informed is the secret Key of the IAM User of amazon s3? – Routfin Mar 02 '23 at 18:47
  • No, not at all, don't put your secret there. That "key" is simply the "filename" what the object will have after uploading. Like in ..../mybucket/mykey. – tevemadar Mar 02 '23 at 18:53
  • I understood. This key I must send to my database in url? Because I would like a url inside my model that references the image saved on amazon s3 – Routfin Mar 02 '23 at 18:55
  • Yes, while your bucket may be hardcoded, the key is going to be unique for each object. For your credentials: you already used them when creating the `S3Client` object. While I haven't tried, this seems to be a complete example showing an upload: https://stackoverflow.com/a/72701635/7916438 – tevemadar Mar 02 '23 at 19:01
  • private final String KEY = "/products/images/" + UUID.randomUUID().toString() – Routfin Mar 02 '23 at 19:10
  • this is okay for a key? – Routfin Mar 02 '23 at 19:10
  • Probably yes, https://stackoverflow.com/questions/2513573/how-good-is-javas-uuid-randomuuid – tevemadar Mar 02 '23 at 19:17

0 Answers0