0

I have an app where users can upload audio files (in the WAV format) and then they see some info about them on the frontend. I want a user to be able to click a file and then have it downloaded onto their computer, so they can listen to it and save it. When users initially upload a file, the file gets stored on Amazon S3, and then the S3 URL gets saved to the database. The backend of the application is in Django / Django Rest Framework and the Frontend is in React. With the code I currently have, nothing happens. I am not seeing any errors and nothing gets downloaded.

I know that I can use a tag to download the file, but the problem is that the filename uses the key from S3. I want to be able to control the naming. That is why I am doing this server side.

Django code:

@api_view(['GET'])
def download_audio_file(request, pk):

    audio = Audio.objects.get(pk=pk)

    s3_client = boto3.client(
        's3',
        aws_access_key_id=AWS_ACCESS_KEY_ID,
        aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
        region_name=AWS_REGION
    )

    s3_response_object = s3_client.get_object(Bucket='bucket_name', Key=s3_key+'.wav')
    object_content = s3_response_object['Body'].read()
    response = HttpResponse(object_content, content_type='audio/wav')
    response['Content-Disposition'] = 'attachment; filename="audio.wav'
    return response

React code:

const handleDownloadAudio = (audio) => {

        fetch(API_URL+"api/audio/"+audio.id+"/download", {
            method: 'GET',
            headers: {
                'Accept': 'application/json',
                'X-CSRFToken': Cookie.get('csrftoken')
            },
            credentials: 'include'
        })
        .then(response => {
            if(response.status === 200) {
                return response.blob()
            }
        })
        .then(response => {
            const blob = new Blob([response], {type: 'audio/wav'});
            const downloadUrl = URL.createObjectURL(blob);
            const a = document.createElement("a");
            a.href = downloadUrl;
            a.download = "audio.wav";
            document.body.appendChild(a);
            a.click();
            console.log("Audio downloaded");
        })
    }

This seems functionality seems common, so I'm not sure why I am having so much trouble trying to get it to work. Any help would be appreciated!

Logan Phillips
  • 660
  • 2
  • 10
  • 23

1 Answers1

1

It is possible to set the Content-Disposition header on the file in s3 when uploading the files. This would allow the user's client to directly download the files with the desired name.

A boto3 example:

S3 = boto3.client('S3')
key = 'audio.wav'
disposition = f'attachment; filename="{key}"'
response = S3.put_object(Body=content, Bucket=BUCKET, Key=key, ContentDisposition=disposition)

Example using the django storages backend https://stackoverflow.com/a/65200033/1464664

Older boto exmaple: Amazon S3 Change file download name

Michael Lindsay
  • 1,290
  • 1
  • 8
  • 6