Uploading Files Directly to S3 with Django and Heroku

I'm currently setting up direct-to-S3 upload in Django for handling larger audio files. I followed the Direct to S3 Tutorial provided by Heroku here, although it's primarily focused on Flask. However, I am encountering difficulty locating the GET request that Django debug is pointing to. Thank you in advance for any assistance.

Here's the error I'm receiving:


Environment:


Request Method: GET
Request URL: http://127.0.0.1:8000/sign-s3/?file_name=piano.wav&file_type=audio/wav

Django Version: 2.0
Python Version: 3.6.5
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.sitemaps',
 'django.contrib.sites',
 'crispy_forms',
 'tinymce',
 'storages',
 'ckeditor',
 'ckeditor_uploader',
 'main',
 'contact',
 'accounts',
 'events',
 'news',
 'project_settings',
 'django_countries',
 'publications',
 'event_tracker',
 'event_signup']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/Users/Tommy/anaconda3/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  35.             response = get_response(request)

File "/Users/Tommy/anaconda3/lib/python3.6/site-packages/django/utils/deprecation.py" in __call__
  97.             response = self.process_response(request, response)

File "/Users/Tommy/anaconda3/lib/python3.6/site-packages/django/middleware/clickjacking.py" in process_response
  26.         if response.get('X-Frame-Options') is not None:

Exception Type: AttributeError at /sign-s3/
Exception Value: 'str' object has no attribute 'get'

Views.py File:

def committee_new_podcast(request, id):
    template = "main/add_podcast.html"

    committee = get_object_or_404(Committee, comm_id=id)

    if request.user.is_authenticated:
        if Member.objects.filter(user=request.user).exists():
            member = Member.objects.get(user=request.user)

            if CommitteeMember.objects.filter(Q(is_chair=True) | Q(is_deputy=True)).filter(abila=member, comm=committee.comm_id) or request.user.is_staff:
                if request.method == 'POST':
                    form = CommitteePodcastForm(request.POST, request.FILES)

                    if form.is_valid():
                        instance = form.save(commit=False)
                        instance.comm_id = committee
                        instance.member = member
                        instance.sound = request.form['avatar-url']
                        instance.save()
                        messages.success(request, 'Committee Podcast Was Created',
                                         "alert alert-success alert-dismissible")

                else:
                    form = CommitteePodcastForm()

            else:
                form = ''
                messages.success(request, 'You must be a chair or deputy to access this page.',
                                 "alert alert-danger alert-dismissible")
        elif request.user.is_staff:
            if request.method == 'POST':
                form = CommitteePodcastForm(request.POST, request.FILES)

                if form.is_valid():
                    instance = form.save(commit=False)
                    instance.comm_id = committee
                    instance.save()
                    messages.success(request, 'Committee Podcast Was Created',
                                     "alert alert-success alert-dismissible")
            else:
                form = CommitteePodcastForm()
        else:
            form = ''
            pass
    else:
        form = ''
        messages.success(request, 'You must be logged in to access this page.',
                         "alert alert-danger alert-dismissible")

    context = {
        'committee': committee,
        'form': form,
    }

    return render(request, template, context)


def sign_s3(request):
    S3_BUCKET = settings.AWS_STORAGE_BUCKET_NAME

    file_name = request.GET.get('file_name')
    file_type = request.GET.get('file_type')

    s3 = boto3.client('s3')

    presigned_post = s3.generate_presigned_post(
        Bucket = S3_BUCKET,
        Key = file_name,
        Fields = {"acl": "public-read", "Content-Type": file_type},
        Conditions = [
            {'acl': "public-read"},
            {"Content-Type": file_type}
        ],
        ExpiresIn = 3600
    )

    return json.dumps({
        'data': presigned_post,
        'url': 'https://%s.s3.amazonaws.com/%s' % (S3_BUCKET, file_name),
    })

Javascript:

<script>
    (function() {
        document.getElementById('file_input').onchange = function(){
            var files = document.getElementById('file_input').files;
            var file = files[0];
            if(!file){
                return alert("No file selected.");
            }
            getSignedRequest(file);
        }
    })();

    function getSignedRequest(file){
        var xhr = new XMLHttpRequest();
        xhr.open("GET",  "/sign-s3?file_name="+file.name+"&file_type="+file.type);
        xhr.onreadystatechange = function(){
            if(xhr.readyState === 4){
                if(xhr.status === 200){
                    var response = JSON.parse(xhr.responseText);
                    uploadFile(file, response.data, response.url);
                }
                else{
                    alert("Could not get signed URL.");
                }
            }
        }
        xhr.send();
    }

    function uploadFile(file, s3Data, url){
        var xhr = new XMLHttpRequest();
        xhr.open("POST", s3Data.url);

        var postData = new FormData();
        for(key in s3Data.fields){
            postData.append(key, s3Data.fields[key]);
        }
        postData.append('file', file);

        xhr.onreadystatechange = function() {
            if(xhr.readyState === 4){
                if(xhr.status === 200 || xhr.status == 204){
                    document.getElementById("sound-url").value = url;
                }
                else {
                    alert("Could not upload file.");
                }
            }
        }
        xhr.send(postData)
    }
</script>

Answer №1

After realizing my mistake, I now understand that I should have used HttpResponse to send back JSON data.

def upload_to_s3(request):
    S3_BUCKET = settings.AWS_STORAGE_BUCKET_NAME

    file_name = request.GET.get('file_name')
    file_type = request.GET.get('file_type')

    s3 = boto3.client('s3')

    presigned_post = s3.generate_presigned_post(
        Bucket = S3_BUCKET,
        Key = file_name,
        Fields = {"acl": "public-read", "Content-Type": file_type},
        Conditions = [
            {'acl': "public-read"},
            {"Content-Type": file_type}
        ],
        ExpiresIn = 3600
    )

    response_data = json.dumps({
        'data': presigned_post,
        'url': 'https://%s.s3.amazonaws.com/%s' % (S3_BUCKET, file_name),
    })

    return HttpResponse(response_data, content_type='json')

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What are the steps for including and excluding components in Parallax JS?

When working with Parallax JS: I am trying to modify the components within the <li> tags of my menu, but I am unsure how to do so without restarting the plugin. I cannot seem to find the destroy command. Currently, I am using the JQuery version of ...

The issue with NGX-Bootstrap/Angular Pagination arises when attempting to adjust the maxSize input while the screen view (width) is being altered

Currently, I am utilizing the Pagination component from Valor Software (click here to access). I am interested in adjusting the maxSize input dynamically based on changes in screen width. For reference, please see this example: Click to view example. It ...

Real-time File Updates Display with Node.js and Express.js

Seeking guidance on whether there is a Node module available to utilize the Express module for displaying real-time updates in the browser using data from a file. I have a regularly updated .csv file and I am looking to showcase these updates instantly on ...

Enhancements in the latest version of Django, known as Django 1

I hear that Django's latest release is now version 1.6 and there are many improvements. However, I am curious if Django 1.6 now includes its own built-in "South" application for schema migrations. In the past, when using Django 1.5, I had to install S ...

Move the material ui menu to the far left side of the page

How can I adjust the menu to align with the left side of the page without any gaps? I've tried various methods but it's not moving to the left. Any suggestions? https://i.sstatic.net/jcAes.jpg Here is the relevant code snippet: <Menu ...

Troubleshooting Django Formset Errors Related to Input Names

Looking to retrieve formset errors based on input names in an Ajax form data submission scenario. models.py class Category(models.Model): category = models.CharField(max_length=128) forms.py class CategoryForm(forms.ModelForm): class Meta: m ...

Unusual issue encountered in next.js production: ENOENT error - File or directory not found (image cache)

Encountering an issue with my AWS EC2 deployment of next.js. After running npm run build followed by npm run start, everything appears to be functioning correctly. Initially, there are no webp images in the cache as expected. However, I have noticed that t ...

Use jQuery.on to load the href into a div element

Is there a way to modify the jQuery load function in order to load a PHP page into a div using .on method? Currently, I am loading different PHP pages into a "modal" div using .load. However, each PHP page calls jQueryUI datepicker and when the content ...

Tips for extracting parameters from a JSON String using JavaScript

When attempting to parse a JSON String, I am encountering an issue where the parsed value is coming up as undefined. You can view the code on this jsfiddle link. <input type="submit" onclick=testJSON() value="Test"/> <div i ...

Spinning an object smoothly in the opposite direction of the OrbitControls camera's y rotation in three.js

Is there a way to use OrbitControls in three.js to make a planeMesh always remain facing the camera? I've attempted some code but it's not quite working as expected. I want the planeMesh to slowly readjust its orientation only when the camera mov ...

Is it possible to modify the HTML/CSS of a child component using the parent component in Angular 2+?

Is there a way to dynamically add text and style to a specific row in a child component based on the parent's logic? (parent): <body> <child-component></child-component> </body> (child): <div *ngfor = "let record in r ...

The jQuery selector for items that do not contain

Have you ever wanted to hide list items based on user input in a text box? The ":contains()" jQuery selector can help you achieve this by allowing you to match elements containing specific text. I have a similar goal - I want users to be able to type in a ...

When React object state remains unchanged, the page does not update automatically

i have a state object with checkboxes: const [checkboxarray_final, setCheckboxarray_final] = useState({ 2: ",4,,5,", 9: ",1,", }); i'm working on enabling check/uncheck functionality for multiple checkboxes: these are ...

Why isn't the selected value updating in ion-select Ionic 2 when using Angularjs 2?

Currently, I have the following code that is functioning well up to a certain point. However, I am encountering a small issue that I cannot seem to resolve. The problem lies in updating the ion-select after the user selects an option. Essentially, the UI d ...

Selecting CSS paging in JQuery Jtable

I am currently working with Jquery JTable and encountering an issue with pagination. The numbers displayed on the selects for "Go to Page" and "Row Count" are not appearing correctly. Any suggestions on how I can resolve this? Thank you, Nk ...

Experiencing a problem with obtaining the .offset().top value of an element within an iframe resulting in an

Encountering an issue with Firefox and Safari where attempting to retrieve the .offset().top value from an element within an iframe results in undefined when trying to access this value. This functionality seems to be working properly in Chrome, Edge, and ...

What is the best way to generate a Google chart inside a newly added element using jQuery?

Currently, I am in the process of constructing a webpage that showcases a Google chart for all active sports games happening on that particular day. A data feed will provide information on the number of active games (as shown below, with 3 ongoing games). ...

Ways to clear TextField status

My question is about a Textfield. In the case where the state is null but the text field value is showing in the Textfield. <TextField style={{ width: '65%'}} id="standard-search" ...

Issue with database setup in Django-Pytest setup_method

I am currently working on Ubuntu 14.04 with the following setup: python 2.7.6 django 1.7 [I have also tested with django 1.9] pytest-django 2.8.0 [also tried with 2.9.1] pytest 2.7.2 [also experimented with 2.8.3] Below is a snippet of the test code in ...