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
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.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.htmlIf 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
just use one for different kind of APP
so you don’t create a bucket each time you run the application
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.
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:
https://docs.aws.amazon.com/code-library/latest/ug/java_2_s3_code_examples.html