Uploading files to AWS S3 Bucket using Spring Boot

Posted on Sept. 5, 2019
spring boot
uploading file to s3
2866

In this article,we will learn how to upload the files to s3 bucket using spring boot.

Spring Boot part:

Let’s create Spring Boot project and add amazon dependency.

<dependency>
   <groupId>com.amazonaws</groupId>
   <artifactId>aws-java-sdk</artifactId>
   <version>1.11.133</version>
</dependency>

Now let’s add s3 bucket properties to our application.yml file:

amazonProperties:
  endpointUrl: "https://s3.us-east-1.amazonaws.com"
  accessKey: "aws_access_key"
  secretKey: "aws_secret_key"
  bucketName: "s3_bucket_name"

Now,create our RestController with two request mappings “/uploadFile" and “/deleteFile”.

@RestController
@RequestMapping("/storage/")
public class BucketController {

    private AmazonClient amazonClient;

    @Autowired
    BucketController(AmazonClient amazonClient) {
        this.amazonClient = amazonClient;
    }

    @PostMapping("/uploadFile")
    public String uploadFile(@RequestPart(value = "file") MultipartFile file) {
        return this.amazonClient.uploadFile(file);
    }

    @DeleteMapping("/deleteFile")
    public String deleteFile(@RequestPart(value = "url") String fileUrl) {
        return this.amazonClient.deleteFileFromS3Bucket(fileUrl);
    }
}

Here,uploadFile() method recieves MultipartFile as a RequestPart.

Till now,we don’t have AmazonClient class yet, so let’s create AmazonClient class.

@Service
public class AmazonClient {

    private AmazonS3 s3client;

    @Value("${amazonProperties.endpointUrl}")
    private String endpointUrl;
    @Value("${amazonProperties.bucketName}")
    private String bucketName;
    @Value("${amazonProperties.accessKey}")
    private String accessKey;
    @Value("${amazonProperties.secretKey}")
    private String secretKey;
@PostConstruct
    private void initializeAmazon() {
       AWSCredentials credentials = new BasicAWSCredentials(this.accessKey, this.secretKey);
       this.s3client = new AmazonS3Client(credentials);
}
}

Here, AmazonS3 is a class from amazon dependency. All other fields are just a representation of variables from our application.yml file. The @Value annotation will bind application properties directly to class fields during application initialization.

We added method initializeAmazon() to set amazon credentials to amazon client. Annotation @PostConstruct is needed to run this method after constructor will be called, because class fields marked with @Value annotation is null in the constructor.

S3 bucket uploading method requires File as a parameter, but we have MultipartFile, so we need to add method which can make this convertion.

private File convertMultiPartToFile(MultipartFile file) throws IOException 
{
    File convFile = new File(file.getOriginalFilename());
    FileOutputStream fos = new FileOutputStream(convFile);
    fos.write(file.getBytes());
    fos.close();
    return convFile;
}

Also you can upload the same file many times, so we should generate unique name for each of them. Let’s use a timestamp and also replace all spaces in filename with underscores to avoid issues in future.

private String generateFileName(MultipartFile multiPart) 
{
    return new Date().getTime() + "-" + multiPart.getOriginalFilename().replace(" ", "_");
}

Now let’s add method which uploads file to S3 bucket.

private void uploadFileTos3bucket(String fileName, File file)
 {
    s3client.putObject(new PutObjectRequest(bucketName, fileName, file).withCannedAcl(CannedAccessControlList.PublicRead));
}

In this method we are adding PublicRead permissions to this file. It means that anyone who have the file url can access this file. 

Finally, we will combine all these methods into one general that is called from our controller. This method will save a file to S3 bucket and return fileUrl which you can store to database. 

public String uploadFile(MultipartFile multipartFile) 
{

    String fileUrl = "";
    try 
    {
        File file = convertMultiPartToFile(multipartFile);
        String fileName = generateFileName(multipartFile);
        fileUrl = endpointUrl + "/" + bucketName + "/" + fileName;
        uploadFileTos3bucket(fileName, file);
        file.delete();
    }
    catch (Exception e) 
    {
       e.printStackTrace();
    }
    return fileUrl;
}

The only thing left to add is deleteFile() method.

public String deleteFileFromS3Bucket(String fileUrl) 
{
    String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
    s3client.deleteObject(new DeleteObjectRequest(bucketName + "/", fileName));
    return "Successfully deleted";
}

S3 bucket cannot delete file by url. It requires a bucket name and a file name, that’s why we retrieved file name from url.

Testing :

Let’s test our application by making requests using Postman. We need to choose POST method, in the Body we should select ‘form-data’. As a key we should enter ‘file’ and choose value type ‘File’. Then choose any file from your PC as a value. The endpoint url is: http://localhost:8080/storage/uploadFile.

If you did everything correct then you should get file url in the response body.

And if you open your S3 bucket on Amazon then you should see one uploaded image there.

Now let’s test our delete method. Choose DELETE method with endpoint url: http://localhost:8080/storage/deleteFile. Body type is the same: ‘form-data’, key: ‘url’, and into value field enter the fileUrl created by S3 bucket.

After sending the request you should get the message ‘Successfully deleted’.

 




0 comments

Please log in to leave a comment.