英文:
Amazon aws error with s3 bucket not existing (Spring Boot)
问题
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 secret key and in my access key, I configured in properties with the keys of my authorized IAM user from the Amazon AWS console.
英文:
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.
答案1
得分: 1
上面的回答提到了AWS SDK for Java V1,不建议再使用。查看版本信息,请参考SDK页面:
https://github.com/awsdocs/aws-doc-sdk-examples
不要再查看包含V1代码的AWS S3服务指南,现在请参考AWS代码库,它使用最新的SDK版本 - 对于Java来说,是AWS SDK for Java V2。
有关AWS SDK for Java V2的示例,请参见:
我在许多Spring Boot项目中使用了V2,没有与S3操作的问题。因此,在Spring Boot中使用S3 Java功能,建议升级到V2 - 这是最佳实践。
可能存在的问题更新
我正在仔细查看您的代码。我在您的控制器中看到这个问题:
private final String BUCKET_NAME = "awstockproducts" + System.currentTimeMillis();
您正在使用时间戳更改桶的名称。然后您调用**putObject()**并引用具有时间戳的桶名称。除非您通过这个名称即时创建了一个桶(我没有看到这个逻辑),否则该桶不存在,您会收到异常。
因此,您有两种选择:
- 保持桶名称不变,以便您知道它存在。
- 或者如果必须使用时间戳来创建一个桶,那么使用V2 API和一个等待器在调用**putObject()**之前创建一个桶。然后,您可以引用具有时间戳的桶名称。要了解如何使用V2创建一个新桶并使用等待器,请参阅此处的创建桶示例。注意等待器的使用。这种方法将为每个请求创建一个新的桶。我个人建议使用静态桶名称。
https://docs.aws.amazon.com/code-library/latest/ug/java_2_s3_code_examples.html
英文:
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:
- 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
答案2
得分: 0
为将您的数据(照片、视频、文档等)上传到Amazon S3,您首先必须在AWS的一个区域中创建一个S3存储桶,存储桶是Amazon S3中存储对象的容器。
您需要确保您要使用的存储桶已经存在
在上传文件到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 '{}' 存储桶不存在", bucketName);
s3client.createBucket(bucketName);
}
如果 s3client.createBucket(bucketName);
抛出异常,那意味着您没有权限创建一个存储桶,请使用此文档创建一个:https://docs.aws.amazon.com/AmazonS3/latest/userguide/create-bucket-overview.html
如果您根本没有权限,请与您的AWS管理员联系以为您创建一个。
此外,不要每次都像这样创建一个存储桶:
private final String BUCKET_NAME = "awstockproducts" + System.currentTimeMillis();
只需为不同类型的应用程序使用一个存储桶:
private final String BUCKET_NAME = "awstockproducts-app1-dev";
这样,您不会在每次运行应用程序时都创建一个存储桶。
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论