In this article we're going to be learning how we can easily create an S3 bucket access items stored in there using the Amplify CLI and Amplify.Storage.

Starting Code: https://github.com/SamWSoftware/amplify-react-tutorial-project/tree/amplify-db

Adding  Play/Pause Functionality

To get started we're going to add a way to play and pause each of the songs. For now, this will just change the play button to a pause button. Later we'll be using the Amplify Storage methods to get our MP3 file from S3 and play it directly in our app.

We're going to add a new function to the App component called toggleSong.

const toggleSong = async idx => {
    if (songPlaying === idx) {
        setSongPlaying('');
        return;
    }
    setSongPlaying(idx);
    return
}

To get this working we also need to add a new state variable to the app. This will be used to store the index of the song that is currently playing.

const [songPlaying, setSongPlaying] = useState('');

With this set up we need to make a change to the JSX code to use our new function and variables. Find the first button in the songCard div and we're going to be adding an onClick which calls our new function. We're also going to use a ternary equation to say that if the song that is playing is this song, show a pause icon instead of a play icon.

<IconButton aria-label="play" onClick={() => toggleSong(idx)}>
    {songPlaying === idx ? <PauseIcon /> : <PlayArrowIcon />}
</IconButton>

We'll just need to import the PauseIcon at the top of the file and we'll be ready.

import PauseIcon from '@material-ui/icons/Pause';

Adding the Storage to Amplify

Next, we need to use the Amplify CLI to add the storage to our app. We can start by going into our terminal and running amplify add storage. This will open a CLI where we need to select the following options.

Please select your service = Content (images, audio, video, etc.)
Friendly name for your resource = songStorage
Bucket name = song-storage
Who should have access = Auth Users Only
What access do those users have = Read and Create/Update
Do you want a Lambda trigger?  = No

With that all configured we need to deploy it and we can start that with amplify push which will take a little bit of time to work out what we've changed in our amplify application. You'll then get a little display of all of the resources we have in Amplify. The only change is the creation of our new songStorage resource. We can select Yes to continuing and then it will create our S3 bucket for us.

Accessing the S3 File through the Amplify Storage methods

Once the deployment has finished we can start using that to access our songs. Back in our toggleSong function we are going to add some extra logic.

const toggleSong = async idx => {
    if (songPlaying === idx) {
        setSongPlaying('');
        return;
    }

    const songFilePath = songs[idx].filePath;
    try {
        const fileAccessURL = await Storage.get(songFilePath, { expires: 60 });
        console.log('access url', fileAccessURL);
        setSongPlaying(idx);
        setAudioURL(fileAccessURL);
        return;
    } catch (error) {
        console.error('error accessing the file from s3', error);
        setAudioURL('');
        setSongPlaying('');
    }
};

This is getting the file path from the song data (that was stored in Dynamo) and then using Storage.get(songFilePath, { expires: 60 }); to get an access url for the file. The { expires: 60 } on the end is saying that the url returned is only going to work for 60 seconds. After that you won't be able to access the file with it. This is a useful security measure so people can't share the url to people who shouldn't have access to the files.

Once we have the file, we're also setting that in a new state variable using setAudioURL. At the top of our App we need to add this extra state.

const [audioURL, setAudioURL] = useState('');

If we save this and go back into our app, if we press the play button and open the console we'll see the url being logged out. This is what we're going to use to play the song.

Uploading Our Songs

We're now getting to the point where we need some songs to play. If we go into our AWS account and search for S3 and then search for song , we should find our new S3 bucket. In there we need to create a new folder called public. This is because the files will be public to everyone who has signed into the app. There are other ways of storing data which is private and only viewable by specific people.

Inside that folder we need to upload two songs. I have two copyright free songs that I uploaded called epic.mp3 and tomorrow.mp3. Once they have been uploaded we need to update our Dynamo data to point at those songs.

In Dynamo we can find our Songs-(lots of random charachters) table. Under Items we should have two records. Open up the first one and change the filePath to tomorrow.mp3 and the name to Tomorrow. Save that and open the second song, changing that to "filePath": "epic.mp3" and "name": "Epic" , saving that file too. If you used your own songs then just make sure the filePath matches the file name of your songs.

We can now go back into our app, refresh the page and press play on one of the songs. If we look in our console and copy the url we're given we can paste that into a browser and our song should start playing.

Adding a Media Player to Our App

Now we want to be able to actually play our song from within our app. To do this we're going to use a library called react-player. We need to first install it with npm install --save react-player.

In our app we can then import it at the top of the file.

import ReactPlayer from 'react-player';

If we find our <div className="songCard">, we want to add our player after that component. In the same way we showed the play/pause icons, we're going to show or hide this player based on which song is playing.

<div className="songCard">
    .. ..
</div>
{songPlaying === idx ? (
    <div className="ourAudioPlayer">
        <ReactPlayer
            url={audioURL}
            controls
            playing
            height="50px"
            onPause={() => toggleSong(idx)}
        />
    </div>
) : null}

The ReactPlayer takes a few parameters. The url is just the file url to play, which is the one we get from Amplify Storage. controls allows the user to change the volume or pause the song, playing means the song starts playing as soon as it's loaded and onPause is a listener so we can have the inbuilt pause button work the same as our pause button.

With this all done we can save everything again and open our App. In there if we press play on one of the songs, we should see a player appear and the song will be playing.

Adding a little bit of styling

This works but having the player on the left looks strange. We've already added a ourAudioPlayer class to the div that contains the ReactPlayer so now we can style it. If we open up our App.css file then add this to the bottom it should work great.

.ourAudioPlayer {
    display: flex;
    justify-content: center;
}

Now we have a nicer looking app.

So that's it! You've now got songs streaming straight from an S3 bucket to your App using Amplify Storage.