skip to Main Content

I’m developing a product registration api in spring boot, and in my post method I send an image upload via multipart/form-data, along with a json with my product’s dto. As you can see:

@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 = "awstockproducts" + System.currentTimeMillis();

    public ProductController(ProductService productService, ProductMapper productMapper, CategoryService categoryService, S3Client s3Client) {
        this.productService = productService;
        this.productMapper = productMapper;
        this.categoryService = categoryService;
        this.s3Client = s3Client;
    }
    @PostMapping(value = "/", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<Object> saveProduct (@RequestParam("productDto") String jsonString, @RequestParam("file")MultipartFile file) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            ProductDto productDto = objectMapper.readValue(jsonString, ProductDto.class);

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

            String fileName = "/products/images/" + UUID.randomUUID().toString() + "-" + file.getOriginalFilename();

            s3Client.putObject(PutObjectRequest
                            .builder()
                            .bucket(BUCKET_NAME)
                            .key(fileName)
                            .build(),
                        software.amazon.awssdk.core.sync.RequestBody.fromString("Testing java sdk"));
            return ResponseEntity.status(HttpStatus.CREATED).body(productService.save(productModel));
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.CONFLICT).body(e);
        }
    }

The problem is that I am having problems making the request from my postman

    "localizedMessage": "The specified bucket does not exist (Service: S3, Status Code: 404, Request ID: ZZ27F9CNNTG2RTPR, Extended Request ID: 

Despite the message, checking amazon aws I can see that indeed my bucket exists and has the same name as the one I configured in my application. This is amazonConfig

package com.api.business_manager_api.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

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

    @Value("${aws.accessKeyId}")
    private String accessKeyId;

    @Value("${aws.secretAccessKey}")
    private String secretAccessKey;

    @Bean
    public S3Client s3Client() {
        AwsBasicCredentials awsCreds = AwsBasicCredentials.create(accessKeyId, secretAccessKey);
        return S3Client.builder()
                .region(Region.of(AWS_REGION))
                .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
                .build();
    }
}

In my secretKey and in my AccessKey, I configured in properties with the keys of my authorized IAM user from the amazon aws console.

2

Answers


  1. To upload your data (photos, videos, documents, etc.) to Amazon S3, you must first create an S3 bucket in one of the AWS Regions, a bucket is a container for objects stored in Amazon S3.

    You need to make sure the bucket you’re using already exists

    use this condition to make sure the bucket already exists before uploading a file to S3:

    if (s3Client.doesBucketExist(bucketName)) {
                ObjectMetadata metadata = new ObjectMetadata();
                metadata.setContentType(contentType);
                metadata.setContentLength(stream.available());
    
                s3Client.putObject(new PutObjectRequest(bucketName, key, stream, metadata));
            } else {
                log.warn("AWS S3 '{}' Bucket does not exist", bucketName);
                 s3client.createBucket(bucketName);
            }
    

    if s3client.createBucket(bucketName); throws an exception that means you’re not allowed to create a bucket use this documentation to create one https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-overview.html

    If you don’t have access at all, you need to talk to your AWS admin to create one for you.

    Also don’t create a bucket each time like this

    private final String BUCKET_NAME = "awstockproducts" + System.currentTimeMillis();
    

    just use one for different kind of APP

    private final String BUCKET_NAME = "awstockproducts-app1-dev"
    

    so you don’t create a bucket each time you run the application

    Login or Signup to reply.
  2. The answer above references AWS SDK for Java V1 – which is not recommended to use anymore. See the SDK page here for Version information:

    https://github.com/awsdocs/aws-doc-sdk-examples

    Rather then looking into the AWS S3 Service Guide that contains V1 code, now refer to the AWS Code Library that uses the latest SDK version – which for Java is AWS SDK for Java V2.

    enter image description here

    For AWS SDK for Java V2 examples, see:

    Code examples for Amazon S3 using AWS SDKs

    I have used V2 in many Spring Boot projects and there are no issues working with S3 operations. So to use S3 Java functionality in Spring boot, upgrade to V2 – which is best practice.

    UPDATE ON A POSSIBLE ISSUE

    I am looking at your code much closer. I see this issue in your controller:

    private final String BUCKET_NAME = "awstockproducts" + System.currentTimeMillis();

    You are changing your bucket name with a time stamp. Then you call putObject() and reference that bucket name with a timestamp. Unless you created a bucket on the fly with that name (and I do not see that logic), that bucket does not exist and you get an exception.

    SO you have 2 choices:

    • keep the bucket name static so you know it exists.
    • or if you have to keep a timestamp for a bucket, then use the V2 API and a waiter to create a bucket before calling putObject(). Then you can reference the bucket name with the timestamp. To learn how to create a new bucket with a waiter using V2, see the Create Bucket example here. Notice the user of a waiter. This approach will create a new bucket for each request. I personally would use a static bucket name.

    https://docs.aws.amazon.com/code-library/latest/ug/java_2_s3_code_examples.html

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search