In this tutorial, we’ll be learning to upload files to S3 and records to Dynamo using AWS Amplify React App.
In the previous article, we had our app playing songs straight from S3. To build upon that we first need to create a way to add a song record. We start by creating an Add
button.
{
showAddSong ? <AddSong /> : <IconButton> <AddIcon /> </IconButton>
}
We then also need to add the AddIcon
to our imports.
import AddIcon from '@material-ui/icons/Add';
Now we can move on to creating the new AddSong
component. We can create this at the bottom of our App.jsx
file.
const AddSong = () => {
return (
<div className="newSong">
<TextField label="Title" />
<TextField label="Artist" />
<TextField label="Description" />
</div>
)
}
We also need to add TextField
to our imports from material UI.
import { Paper, IconButton, TextField } from '@material-ui/core';
The next thing to do is add the ability to open our new component by controlling the showAddSong
variable. We need to create a new state declaration next to the others.
const [showAddSong, setShowAddNewSong] = useState(false);
We can now update our new AddIcon
button to set showAddSong
to true.
<IconButton onClick={() => setShowAddNewSong(true)}>
<AddIcon />
</IconButton>
To change it back, we can add a parameter to our AddSong
component called onUpload
. When this gets called we will reset the showAddSong to false.
<AddSong
onUpload={() => {
setShowAddNewSong(false);
}}
/>
We then need to update our component to work with that new parameter and a button to “upload” the new song. That button calls a function in the component where we will add the ability to upload the data, but for now we will just call the onUpload
function.
const AddSong = ({ onUpload }) => {
const uploadSong = async () => {
//Upload the song
onUpload();
};
return (
<div className="newSong">
<TextField
label="Title"
/>
<TextField
label="Artist"
/>
<TextField
label="Description"
/>
<IconButton onClick={uploadSong}>
<PublishIcon />
</IconButton>
</div>
);
};
And now we add the PublishIcon
to our imports and we’re ready to test this out.
import PublishIcon from '@material-ui/icons/Publish';
When we start up the app and log in we now get a plus icon, clicking that we can enter some details for the song and click upload.
Updating AddSong
Now we want to be able to store and access the data that a user enters into the fields when adding a song.
const AddSong = ({ onUpload }) => {
const [songData, setSongData] = useState({});
const uploadSong = async () => {
//Upload the song
console.log('songData', songData);
const { title, description, owner } = songData;
onUpload();
};
return (
<div className="newSong">
<TextField
label="Title"
value={songData.title}
onChange={e => setSongData({ ...songData, title: e.target.value })}
/>
<TextField
label="Artist"
value={songData.owner}
onChange={e => setSongData({ ...songData, owner: e.target.value })}
/>
<TextField
label="Description"
value={songData.description}
onChange={e => setSongData({ ...songData, description: e.target.value })}
/>
<IconButton onClick={uploadSong}>
<PublishIcon />
</IconButton>
</div>
);
};
We’ve also had to change all of the TextFields to be controlled, passing in a value from out state and providing an onChange
too. If we save this and try entering some details before uploading we should see a console.log of the details in our chrome console.
Next we need to add the ability to actually upload the song. For this we’ll be using the default HTML input with a type of file. Add this to the JSX just before the upload icon button.
<input type="file" accept="audio/mp3" onChange={e => setMp3Data(e.target.files[0])} />
As you may have noticed we are calling setMp3Data
on change. This is some more state in the AddSong component.
const [mp3Data, setMp3Data] = useState();
Now we have all of the data that we need, we can start by uploading the song to S3 and then the data to our database. To upload the song we’re going to use the Amplify React App class again. The fileName is going to be a UUID so we also need to run npm install --save uuid
in our terminal and then import it at the top of our file import { v4 as uuid } from 'uuid';
. We then pass in the mp3Data and a contentType and we get back an object with a key.
const { key } = await Storage.put(`${uuid()}.mp3`, mp3Data, { contentType: 'audio/mp3' });
Now we have the key we can create the record for the song in the database. As there may be multiple songs with the same name, we’ll use an UUID as the ID again.
const createSongInput = {
id: uuid(),
title,
description,
owner,
filePath: key,
like: 0,
};
await API.graphql(graphqlOperation(createSong, { input: createSongInput }));
To get this to work we need to import the createSong
mutator that was created when we created the dynamo storage with Amplify.
import { updateSong, createSong } from './graphql/mutations';
The last thing that we need to do is to make the app re-get the data from the database once we’ve finished uploading it. We can do this by adding a fetchSongs
call as part of the onUpload
function.
<AddSong
onUpload={() => {
setShowAddNewSong(false);
fetchSongs();
}}
/>
Now when we reload the page, we can click to add a new song, input the details, select our new song, upload it and then play it back from the app