<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>S3Template Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/s3template/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Wed, 16 Apr 2025 04:49:36 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>S3Template Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Spring Boot with Kotlin, AWS S3, and S3Template</title>
		<link>https://blog.codersee.com/spring-boot-with-kotlin-aws-s3-and-s3template/</link>
					<comments>https://blog.codersee.com/spring-boot-with-kotlin-aws-s3-and-s3template/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Tue, 17 Sep 2024 05:00:00 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[S3 Object Storage]]></category>
		<category><![CDATA[S3Template]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=12009151</guid>

					<description><![CDATA[<p>The second article in a series dedicated to Spring Boot AWS S3 integration focused on S3Template and Kotlin.</p>
<p>The post <a href="https://blog.codersee.com/spring-boot-with-kotlin-aws-s3-and-s3template/">Spring Boot with Kotlin, AWS S3, and S3Template</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>Welcome to the <strong>second article</strong> in a series dedicated to integrating a Spring Boot Kotlin app with <strong>AWS S3 </strong>Object Storage, in which we will learn how to make our lives easier with <strong>S3Template</strong>. </p>



<p>Of course, I highly encourage you to take a look at other articles in this series, too: </p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/spring-boot-aws-s3-s3client-kotlin/">#1 Spring Boot with AWS S3, S3Client, and Kotlin</a></li>



<li>#2 (This article)</li>



<li><a href="https://blog.codersee.com/test-spring-boot-aws-s3-with-localstack-and-testcontainers/">#3 Test Spring Boot AWS S3 with Localstack and Testcontainers</a></li>
</ul>



<h2 class="wp-block-heading" id="h-video-tutorial">Video Tutorial</h2>



<p>If you prefer <strong>video content</strong>, then check out my video that covers all three articles:</p>



<div style="text-align: center; width: 90%; margin-left: 5%;">
<a href="https://blog.codersee.com/spring-boot-with-kotlin-aws-s3-and-s3template/"><img decoding="async" src="https://blog.codersee.com/wp-content/plugins/wp-youtube-lyte/lyteCache.php?origThumbUrl=%2F%2Fi.ytimg.com%2Fvi%2FuTV9w1JehHM%2Fhqdefault.jpg" alt="YouTube Video"></a><br /><br /></p></div>



<p>If you find this content useful,<strong> please leave a subscription</strong> 🙂</p>



<h2 class="wp-block-heading" id="h-what-is-s3template">What is S3Template? </h2>



<p>Before we get our hands dirty with the code, let&#8217;s take a second to understand better with S3Template is and how it can make our lives easier when integrating a Spring Boot app with S3. </p>



<p>Let me quote the S3Template documentation here: </p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Higher level abstraction over S3Client providing methods for the most common use cases.</p>



<p>So, if you have already worked with, or you have seen my previous article with S3Client, then you saw that simple operations require some boilerplate. And that is exactly what S3Template solves. </p>



<p>And as a note from my end, I just wanted to note S3Template handles only some subset of S3Client operations, so in our projects those two will rather coexist, instead of being each others alternatives. </p>
</blockquote>



<h2 class="wp-block-heading" id="h-aws-s3template-operations">AWS S3Template Operations</h2>



<p>With all of that said, let&#8217;s get to work. </p>



<p>Let&#8217;s add the <code>controller</code> package and <code>BucketController</code> class to it. We will use it to expose a bunch of endpoints triggering various operations on S3 buckets and files. </p>



<p>When it comes to the operations- we will use the same ones as in the previous article, and as the last one, I will show you how to <strong>serialize and deserialize objects with AWS S3 and S3Template.</strong></p>



<h3 class="wp-block-heading" id="h-list-all-buckets">List All Buckets</h3>



<p>Although the S3Template does not expose any method that would help us with this task, I wanted to mention it as we discussed it in the previous tutorial. </p>



<p>Additionally, this is a great example of <code>S3Client</code> and <code>S3Template</code> co-existence: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@RestController
@RequestMapping("/buckets")
class BucketController(
  private val s3Template: S3Template,
  private val s3Client: S3Client,
) {

  @GetMapping
  fun listBuckets(): List&lt;String> {
    val response = s3Client.listBuckets()

    return response.buckets()
      .mapIndexed { index, bucket ->
        "Bucket #${index + 1}: ${bucket.name()}"
      }
  }
}</pre>



<p>As we can see, not too much S3Template could improve here, so I bet this is the reason why it was not introduced. </p>



<h3 class="wp-block-heading" id="h-new-s3-bucket">New S3 Bucket</h3>



<p>Nextly, let&#8217;s take a look at how we can create a brand new bucket: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@PostMapping
fun createBucket(@RequestBody request: BucketRequest) {
  s3Template.createBucket(request.bucketName)
}

data class BucketRequest(val bucketName: String)</pre>



<p>As we can see, no additional request classes- the only thing we need is the bucket name. </p>



<p>Of course, let&#8217;s rerun our application and verify if everything is working: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'http://localhost:8080/buckets' \
--header 'Content-Type: application/json' \
--data-raw '{
    "bucketName": "codersee-awesome-bucket"
}'</pre>



<p>As a result, we should get 200 OK without a response body. </p>



<h3 class="wp-block-heading" id="h-upload-file-to-s3-bucket">Upload File to S3 Bucket</h3>



<p>Nextly, let&#8217;s take a look at how to upload a new file to S3. </p>



<p>We have a few options, among which the <code>store</code> function is the easiest: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@PostMapping("/{bucketName}/objects")
fun createObject(@PathVariable bucketName: String, @RequestBody request: ObjectRequest) {
  s3Template.store(bucketName, request.objectName, request.content)
}

data class ObjectRequest(val objectName: String, val content: String)</pre>



<p>As we can see, this function takes three arguments: </p>



<ul class="wp-block-list">
<li>the bucket name, </li>



<li>filename,</li>



<li>and the content to upload (to be specific <code>Object object</code>) </li>
</ul>



<p>Alternatively, we could use the <code>upload</code> function, which allows us to send <code>InputStream</code> instance and metadata: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="java" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@Override
public S3Resource upload(
  String bucketName, 
  String key, 
  InputStream inputStream,
  @Nullable ObjectMetadata objectMetadata
) </pre>



<p>Lastly, let&#8217;s verify that everything is fine with the following curl: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'http://localhost:8080/buckets/codersee-awesome-bucket/objects' \
--header 'Content-Type: application/json' \
--data-raw '{
    "objectName": "file-example.txt",
    "content": "My file content"
}'</pre>



<p>If everything worked, then a new file should be present in our bucket 🙂 </p>



<h3 class="wp-block-heading" id="h-list-files-from-the-bucket">List Files From The Bucket</h3>



<p>Nextly, let&#8217;s see how we can list the bucket content with <em>S3Template</em>: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@GetMapping("/{bucketName}/objects")
fun listObjects(@PathVariable bucketName: String): List&lt;String> =
  s3Template.listObjects(bucketName, "")
    .map { s3Resource -> s3Resource.filename }</pre>



<p>We can clearly see that this is not rocket science 😉 </p>



<p>Nevertheless, it is worth mentioning that this time we get the <code>S3Resource</code> instance and instead of the <code>key()</code> we use it <code>getFilename</code> method. </p>



<p>And just like previously, let&#8217;s see the endpoint in action: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'http://localhost:8080/buckets/codersee-awesome-bucket/objects' \
--header 'Content-Type: application/json' \
--data-raw '{
    "objectName": "file-example.txt",
    "content": "My file content"
}'

# Response status: 200 OK
# Response body: 
[
    "file-example.txt"
]</pre>



<h3 class="wp-block-heading" id="h-download-a-file">Download a File</h3>



<p>So what about fetching files from the S3 bucket? </p>



<p>With <em>S3Template</em>, it&#8217;s a piece of cake: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@GetMapping("/{bucketName}/objects/{objectName}")
fun getObject(@PathVariable bucketName: String, @PathVariable objectName: String): String =
  s3Template.download(bucketName, objectName).getContentAsString(UTF_8)</pre>



<p>We specify the bucket name and the object name, and as a result, we get the <code>S3Resource</code> that exposes a bunch of methods. Among others, the <code>getContentAsString</code> that is pretty descriptive 😉 </p>



<p>Similarly, let&#8217;s hit the endpoint: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request GET 'http://localhost:8080/buckets/codersee-awesome-bucket/objects/file-example.txt'

# Response status: 200 OK
# Response body: 
"My file content"</pre>



<h3 class="wp-block-heading" id="h-delete-the-bucket">Delete the Bucket</h3>



<p>Last before least, let&#8217;s take a look at how we can get rid of the bucket: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@DeleteMapping("/{bucketName}")
fun deleteBucket(@PathVariable bucketName: String) {
  s3Template.listObjects(bucketName, "")
    .forEach { s3Template.deleteObject(bucketName, it.filename) }

  s3Template.deleteBucket(bucketName)
}</pre>



<p>And just like in the previous article- we must ensure the bucket does not contain any objects. </p>



<p>To do so, we list out objects by specifying the bucket name and objects prefix (as we don&#8217;t have any, we pass an empty String). Then, for each object, we use its key to delete it. And lastly, we simply invoke the <code>deleteBucket</code> by passing the name of a bucket to delete. </p>



<p>Of course, let&#8217;s verify this logic, too: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request DELETE 'http://localhost:8080/buckets/codersee-awesome-bucket'</pre>



<p>If we run this command and the S3 bucket exists, then we should see 200 OK and our bucket will disappear. </p>



<h3 class="wp-block-heading" id="h-serialize-deserialize-objects">Serialize/Deserialize objects </h3>



<p>As the last thing, let&#8217;s take a look at how easily we can persist objects using the combination of <code>store</code> and <code>read</code> functions: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="kotlin" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">@PostMapping("/{bucketName}/objects")
fun createExampleObject(@PathVariable bucketName: String): Example {
  val example = Example(id = UUID.randomUUID(), name = "Some name")

  s3Template.store(bucketName, "example.json", example)

  return s3Template.read(bucketName, "example.json", Example::class.java)
}

data class Example(val id: UUID, val name: String)</pre>



<p>As we can see, we made a small update to our <code>POST /{bucketName}/objects</code> endpoint logic. </p>



<p>Basically, the first part is exactly the same, we use the <code>store</code> again to push the file to the bucket. </p>



<p>Nevertheless, instead of the <code>download</code> we saw previously, we use the <code>read</code> function that uses the <code>S3ObjectConverter</code> that will automatically deserialize the JSON into the <code>Example</code> class instance. </p>



<p>And for the last time, let&#8217;s hit our API: </p>



<pre class="EnlighterJSRAW" data-enlighter-language="raw" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">curl --location --request POST 'http://localhost:8080/buckets/codersee-awesome-bucket/objects' \
--data-raw ''

# Response status: 200 OK 
# Response body: 
{
    "id": "3eacd8a3-48b2-4756-86db-e4c9f4e291da",
    "name": "Some name"
}</pre>



<p>And as we can see, the output confirms that everything is working fine. </p>



<p>Summary</p>



<p>And that&#8217;s all for this article on how to make our lives easier in Spring Boot with S3Template. </p>



<p>I hope you enjoyed it, and again wanted to invite you to take a look at other content of this series: </p>



<ul class="wp-block-list">
<li><a href="https://blog.codersee.com/spring-boot-aws-s3-s3client-kotlin/">#1 Spring Boot with AWS S3, S3Client, and Kotlin</a></li>



<li>#2 (This article)</li>



<li><a href="https://blog.codersee.com/test-spring-boot-aws-s3-with-localstack-and-testcontainers/">#3 Test Spring Boot AWS S3 with Localstack and Testcontainers</a></li>
</ul>



<p>Lastly, just wanted to show that you can find the source code in <a href="https://github.com/codersee-blog/spring-boot-3-kotlin-s3template" target="_blank" rel="noreferrer noopener">this GitHub repository</a> and that you can join my <a href="https://codersee.com/newsletter/">newsletter</a> to stay up-to-date with Kotlin on the backend. </p>
<p>The post <a href="https://blog.codersee.com/spring-boot-with-kotlin-aws-s3-and-s3template/">Spring Boot with Kotlin, AWS S3, and S3Template</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/spring-boot-with-kotlin-aws-s3-and-s3template/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-05-19 14:47:01 by W3 Total Cache
-->