<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged cloud-storage</title>
<link>https://coderlegion.com/tag/cloud-storage</link>
<description>Powered by Question2Answer</description>
<item>
<title>Upload Files to AWS S3 with Spring Boot (Clean Architecture Guide)</title>
<link>https://coderlegion.com/14577/upload-files-to-aws-s3-with-spring-boot-clean-architecture-guide</link>
<description>&lt;p&gt;Uploading files to AWS S3 in Spring Boot is simple.&lt;/p&gt;
&lt;p&gt;Designing it properly for production is where most developers struggle.&lt;/p&gt;
&lt;p&gt;Most implementations end up like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;controllers handling everything&lt;/li&gt;
&lt;li&gt;direct S3 calls everywhere&lt;/li&gt;
&lt;li&gt;no structure&lt;/li&gt;
&lt;li&gt;hard to scale later&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In this guide, you’ll learn how to build a clean, scalable file upload system.&lt;/p&gt;
&lt;p&gt;Building file uploads in production is harder than it looks.&lt;/p&gt;
&lt;p&gt;If you want a clean, ready-to-use setup instead of building everything yourself:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://buildbasekit.com/boilerplates/filora-fs-pro/&quot;&gt;https://buildbasekit.com/boilerplates/filora-fs-pro/&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;quick-answer&quot;&gt;Quick Answer&lt;/h3&gt;
&lt;p&gt;The best way to upload files to S3 in Spring Boot:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;separate storage logic into a service layer  &lt;/li&gt;
&lt;li&gt;avoid direct S3 calls in controllers  &lt;/li&gt;
&lt;li&gt;use pre-signed URLs for scalability  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-use-aws-s3&quot;&gt;Why use AWS S3&lt;/h3&gt;
&lt;p&gt;S3 is the standard for file storage in backend systems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;handles large files and high traffic  &lt;/li&gt;
&lt;li&gt;highly scalable and durable  &lt;/li&gt;
&lt;li&gt;no infrastructure management  &lt;/li&gt;
&lt;li&gt;supports secure access control  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;when-to-use-s3-vs-local-storage&quot;&gt;When to use S3 vs local storage&lt;/h3&gt;
&lt;p&gt;Use S3 when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;building production systems  &lt;/li&gt;
&lt;li&gt;handling user uploads  &lt;/li&gt;
&lt;li&gt;running multiple instances  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use local storage when:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;prototyping  &lt;/li&gt;
&lt;li&gt;small internal tools  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Avoid local storage in distributed systems.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;high-level-architecture&quot;&gt;High-level architecture&lt;/h3&gt;
&lt;p&gt;A clean file upload system should look like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Controller → handles incoming requests  &lt;/li&gt;
&lt;li&gt;Service → processes file logic  &lt;/li&gt;
&lt;li&gt;Storage layer → interacts with S3  &lt;/li&gt;
&lt;li&gt;Database → stores file metadata  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This separation keeps your code maintainable.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;file-metadata-design&quot;&gt;File metadata design&lt;/h3&gt;
&lt;p&gt;Do not just upload files.&lt;/p&gt;
&lt;p&gt;Store metadata like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;file name  &lt;/li&gt;
&lt;li&gt;unique ID  &lt;/li&gt;
&lt;li&gt;S3 object key  &lt;/li&gt;
&lt;li&gt;file size and type  &lt;/li&gt;
&lt;li&gt;upload timestamp  &lt;/li&gt;
&lt;li&gt;user reference  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This enables search, access control, and tracking.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;example-api-design&quot;&gt;Example API design&lt;/h3&gt;
&lt;p&gt;A simple API structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;POST /files → upload file  &lt;/li&gt;
&lt;li&gt;GET /files/{id} → fetch metadata  &lt;/li&gt;
&lt;li&gt;GET /files/{id}/url → get access URL  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;upload-flow&quot;&gt;Upload flow&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;client sends upload request  &lt;/li&gt;
&lt;li&gt;backend validates file  &lt;/li&gt;
&lt;li&gt;file is uploaded to S3  &lt;/li&gt;
&lt;li&gt;metadata is stored in database  &lt;/li&gt;
&lt;li&gt;response returns file reference  &lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;keep-storage-logic-abstract&quot;&gt;Keep storage logic abstract&lt;/h3&gt;
&lt;p&gt;Do not tightly couple your app with S3.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;define a storage interface  &lt;/li&gt;
&lt;li&gt;implement S3 separately  &lt;/li&gt;
&lt;li&gt;allow switching providers later  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This makes your system flexible.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;security-considerations&quot;&gt;Security considerations&lt;/h3&gt;
&lt;p&gt;Always handle security properly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;validate file type and size  &lt;/li&gt;
&lt;li&gt;avoid exposing S3 bucket directly  &lt;/li&gt;
&lt;li&gt;manage credentials securely  &lt;/li&gt;
&lt;li&gt;use pre-signed URLs  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;pre-signed-urls-important-&quot;&gt;Pre-signed URLs (important)&lt;/h3&gt;
&lt;p&gt;Better approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;backend generates upload URL  &lt;/li&gt;
&lt;li&gt;client uploads directly to S3  &lt;/li&gt;
&lt;li&gt;backend stores metadata  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;reduces server load  &lt;/li&gt;
&lt;li&gt;improves scalability  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;common-mistakes&quot;&gt;Common mistakes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;calling S3 directly from controller  &lt;/li&gt;
&lt;li&gt;hardcoding credentials  &lt;/li&gt;
&lt;li&gt;skipping metadata storage  &lt;/li&gt;
&lt;li&gt;mixing upload and access logic  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;Uploading files to S3 is easy.&lt;/p&gt;
&lt;p&gt;Designing a clean system is what matters long term.&lt;/p&gt;
&lt;p&gt;If you skip structure now, it will become painful later.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;skip-the-setup&quot;&gt;Skip the setup&lt;/h3&gt;
&lt;p&gt;If you're building anything serious, don't spend days structuring this.&lt;/p&gt;
&lt;p&gt;Use a production-ready backend instead:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://buildbasekit.com/boilerplates/filora-fs-pro/&quot;&gt;https://buildbasekit.com/boilerplates/filora-fs-pro/&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;clean architecture  &lt;/li&gt;
&lt;li&gt;S3 + pre-signed URLs  &lt;/li&gt;
&lt;li&gt;ready APIs  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Build faster. Focus on real features.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/14577/upload-files-to-aws-s3-with-spring-boot-clean-architecture-guide</guid>
<pubDate>Sun, 12 Apr 2026 16:42:50 +0000</pubDate>
</item>
</channel>
</rss>