Amplify Storage isn't set up by default, so you'll need to do a couple of steps to get it working.
storage/
inside your amplify/
folder.resource.ts
file inside that.import { defineStorage } from '@aws-amplify/backend';
export const storage = defineStorage({
name: 'siteAssets'
});
The amplify backend needs to know about the new file.
amplify/backend.ts
.import {storage} from './storage/resource'
storage
within the defineBackend()
method.import {defineBackend} from '@aws-amplify/backend';
import {auth} from './auth/resource';
import {data} from './data/resource';
import {storage} from './storage/resource'
defineBackend({
auth,
data,
storage
});
In order for any files to be accessed through the app, you'll need to define file path access within amplify/storage/resource.ts
.
When defining a storage resource in Amplify Gen 2 using defineStorage, you control who can access which files using the access configuration. This is a declarative way to specify permissions for different folders (key prefixes) and user types.
access
Keywordpublic/media/{id}/*
) to arrays of access rules.Example:
export const storage = defineStorage({
name: 'myProjectFiles',
access: (allow) => ({
'public/media/{id}/*': [
allow.guest.to(['read'])
]
})
});
You specify the allowed actions as an array:
action | description |
---|---|
read | Download or list files |
write | Upload or overwrite files |
delete | Remove files |
The allow
object provides several methods for specifying who can access files:
Example:
allow.guest.to(['read']);
Example:
allow.authenticated.to(['read', 'write'])
Example:
allow.groups(['Admin', 'Editor']).to(['read', 'write', 'delete'])
Also ensure that the groups are defined in amplify/auth/resource.ts
import { defineAuth } from '@aws-amplify/backend';
export const auth = defineAuth({
loginWith: {
email: true
},
groups: ['Admin', 'Editor']
});
Example:
allow.entity('myLambdaFunction').to(['read', 'write'])
This is more advanced and often not needed for simple user-based apps.
Amplify Gen 2 Storage creates three logical prefixes at the root of your bucket:
prefix | description |
---|---|
public/ |
accessible to everyone with allowed permissions |
protected/ |
accessible to signed-in users and admins |
private/ |
accessible only to the file’s owner |
To avoid collisions, support easy retrieval, and promote scaling, separate each object by its identifier and nest any related files within each object. For an e-commerce app with a Product
table, it may look like the following:
public/products/{productId}/main.jpg
public/products/{productId}/gallery/1.jpg
public/products/{productId}/gallery/2.jpg
public/products/{productId}/manual.pdf
productId
: A unique identifier for each product (e.g., UUID or SKU).main.jpg
: The primary image for the product.gallery/
: A folder containing additional images for the product.manual.pdf
: A product manual or other associated document.public/
products/
123e4567/
main.jpg
manual.jpg
gallery/
1.jpg
2.jpg
abcdef12/
main.jpg
manual.jpg
gallery/
1.jpg
2.jpg
3.jpg
sitegraphics/
logo.png
favicon.ico
There is a React Component that makes image retrieval super streamlined. Simply install the npm package and use the <StorageImage />
Component to retrieve an image.
npm add @aws-amplify/ui-react-storage aws-amplify
import { StorageImage } from '@aws-amplify/ui-react-storage';
export const DefaultStorageImageExample = () => {
return <StorageImage alt="cat" path="your-path/cat.jpg" />;
};
Assuming you have a model like the following:
const schema = a.schema({
Product: a
.model({
name: a.string().required(),
description: a.string(),
images: a.customType({
main: a.string().required(),
manual: a.string().required()
})
You could map through all of the "main" images like this:
import {StorageImage} from "@aws-amplify/ui-react-storage";
type Product = Schema['Product']['type'];
const client = generateClient<Schema>({authMode: "apiKey"});
export const App = () => {
const [products, setProducts] = useState<Product[]>([]);
useEffect(() => {
const sub = client.models.Product.observeQuery().subscribe({
next: ({items}) => {
setProducts([...items]);
}
});
return () => sub.unsubscribe();
}, []);
return (
<ul>
{products.map((product) => (
<li>
<StorageImage
path={"public/" + product.images.main}
alt={product.name}
className={"product-image"}
/>
</li>
))}
</ul>
);
}