Django Admin File field ( Widget ) for AWS Identity based S3 uploads

(0 comments)

When you are working with AWS serverless, You probably has faced body size limit for your lambda function. Basically It won't allow file uploads beyond size limit specified. 
Here is guide if you are using Django in your website and deployed on AWS Lambda + API Gateway ( Zappa ) And want to allow file uploads of any size in Django Admin using AWS S3 Identity based uploads.

Write One API which give response as following json : 
     `/api/awsIdentity/` // any endpoint you like make sure authenticated and GET only
     {

          "IdentityId": "",
          "Token": "",
          "bucket_name": "",
          "bucket_region": "",
          "auth_role_arn": ""
     }

Your widget for File Field And Admin Form will looks like this : 

    from django import forms
    from django.contrib import admin

    class AWSIdentityS3FileWidget(forms.Textarea):
        template_name = 's3_identity_file_field.html'
        # GET and for logged in users only
        identity_api_url = "/api/awsIdentity/"

        def __init__(self, *args, **kwargs):
            super(AWSIdentityS3FileWidget, self).__init__(*args, **kwargs)
            self.attrs['identity_api_url'] = self.identity_api_url
            self.attrs['rows'] = 2

    class YourModelForm(forms.ModelForm):
        class Meta:
            model = YourModel
            exclude = ()

        your_file = forms.CharField(
            label='Your File', required=False,
            widget=AWSIdentityS3FileWidget())

        def save(self, commit=True):
            if self.cleaned_data.get("your_file"):
                # This will set your file to public access
                # using boto3
                set_my_file_as_public(self.cleaned_data.get("your_file"))
         return super(YourModelForm, self).save(commit=commit)


    class YourModelAdmin(admin.ModelAdmin):
        form = YourModelForm

Your Widget HTML will be :  `s3_identity_file_field.html`

    <script src="https://sdk.amazonaws.com/js/aws-sdk-2.82.0.min.js"></script>
    <input type="file" id="file-chooser" class="toggle_hide_display" />
    <textarea class="toggle_hide_display" readonly="true" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
    {% if widget.value %}{{ widget.value }}{% endif %}</textarea>
    <span id="loading_widget"></span>
    {% if widget.value %} <span class="toggle_hide_display">Remove File : </span> <input type="checkbox" id="empty_text_area" class="toggle_hide_display" />{% endif %}
    <script>
    if (!$) {
       $ = django.jQuery;
    }
    $("#loading_widget").text('Loading ...').show();
    $('.toggle_hide_display').hide();
    $.getJSON("{{widget.attrs.identity_api_url}}", function( data ) {

        var bucketName = data.bucket_name;
        var bucketRegion = data.bucket_region;
        var IdentityId = data.IdentityId;
        var webtoken = data.Token;
        var role_arn = data.auth_role_arn;
        var bucket = null;

        var params = {
            RoleArn: role_arn,
            WebIdentityToken: webtoken
        };
        AWS.config.region = bucketRegion;
        AWS.config.credentials = new AWS.WebIdentityCredentials(params, function(err) {
            console.log(err, err.stack);
        });

        AWS.config.credentials.get(function(err) {
        if (err) alert(err);
            bucket = new AWS.S3({
                params: {
                    Bucket: bucketName
                },
                httpOptions: {
                    timeout: 600000
                }
            });
            $("#loading_widget").hide();
            $('.toggle_hide_display').show();
        });

        $('#empty_text_area').on('change', function(ele){
            if($('input[id="empty_text_area"]:checked').length > 0){
                $('#{{widget.attrs.id}}').val('');
            }
        });
        var fileChooser = document.getElementById('file-chooser');
        fileChooser.addEventListener('change', function() {
            $("#loading_widget").text('Uploading ...').show();
            var file = fileChooser.files[0];
            if (file) {
                var objKey = 'public/' + IdentityId + '/' + file.name;
                var params = {
                    Key: objKey,
                    ContentType: file.type,
                    Body: file
                };
                bucket.putObject(params, function(err, data) {
                if (err) {
                    console.log('error upload', data);
                } else {
                    $('#{{widget.attrs.id}}').val(objKey);
                }
                $("#loading_widget").hide();
            });
            } else {
                console.log('error choose file');
            }
        }, false);
    });
</script>

Hope Its working for you. leave comment if not. 

Current rating: 5

Comments

There are currently no comments

New Comment

required

required (not published)

optional

required

captcha

required