Skip to content

Commit

Permalink
Presign File for Uploading to S3 Via Boto3
Browse files Browse the repository at this point in the history
  • Loading branch information
codingforentrepreneurs committed Jan 15, 2024
1 parent b65c12e commit 76bb480
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/items/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
urlpatterns = [
path("", views.item_list_view, name='list'),
path("<int:id>/", views.item_detail_update_view, name='detail'),
path("<int:id>/upload/", views.item_upload_view, name='upload'),
path("<int:id>/files/", views.item_files_view, name='files'),
re_path(r'^(?P<id>\d+)/files/(?P<name>.*)$', views.item_file_delete_view, name='files_delete'),
path("<int:id>/edit/", views.item_detail_inline_update_view, name='edit'),
Expand Down
45 changes: 44 additions & 1 deletion src/items/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import mimetypes
from cfehome.env import config
from django.contrib.auth.decorators import login_required
from django.http import QueryDict, HttpResponse
from django.http import QueryDict, HttpResponse, JsonResponse
from django.shortcuts import redirect, render, get_object_or_404
from django.urls import reverse
from projects import cache as projects_cache
from projects.decorators import project_required
from django.utils.text import slugify

from django_htmx.http import HttpResponseClientRedirect

Expand All @@ -22,6 +23,48 @@
AWS_BUCKET_NAME=config("AWS_BUCKET_NAME", default=None)


def filename_to_s3_filename(fname):
if fname is None:
return None
if fname == '':
return None
stem = pathlib.Path(fname).stem
suffix = pathlib.Path(fname).suffix
stem_clean = slugify(stem).replace('-', '_')
return f'{stem_clean}{suffix}'

@project_required
@login_required
def item_upload_view(request, id=None):
instance = get_object_or_404(Item, id=id, project=request.project)
# if not request.htmx:
# detail_url = instance.get_absolute_url()
# return redirect(detail_url)
template_name = 'items/file-upload.html'
if request.method == "POST":
print(request.POST)
file_name = request.POST.get('file_name')
name = filename_to_s3_filename(file_name)
if name is None:
"""
Invalid name, alert user
"""
return JsonResponse({"url": None})
client = s3.S3Client(
aws_access_key_id=AWS_ACCESS_KEY_ID,
aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
default_bucket_name=AWS_BUCKET_NAME,
).client
prefix = instance.get_prefix()
key = f"{prefix}{name}"
url = client.generate_presigned_url('put_object', Params={"Bucket": AWS_BUCKET_NAME, "Key": key}, ExpiresIn=3600)
return JsonResponse({"url": url, 'filename': name})
return render(request, template_name,
{
"instance": instance}
)


@project_required
@login_required
def item_file_delete_view(request, id=None, name=None):
Expand Down
63 changes: 63 additions & 0 deletions src/templates/items/file-upload.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{% extends 'base.html' %}


{% block content %}

<div class="flex space-x-4">
<div class="grow">
<form method="post" id="upload-form">
<input type="hidden" value="{{ request.build_absolute_uri }}" name="endpoint" />
<input type="file" id="file-id" name="file" />
<button type="submit" id="upload-button" >Upload to S3</button>
</form>

</div>
</div>

<script>
const uploadForm = document.getElementById('upload-form')
if (uploadForm) {
uploadForm.addEventListener("submit", handleSubmitEvent)
}

function handleSubmitEvent(event) {
event.preventDefault()
const fileInput = uploadForm.querySelector('[name="file"]')
const endpointInput = uploadForm.querySelector('[name="endpoint"]')
const signingEndpoint = endpointInput.value
const file = fileInput.files[0]
const hxHeaders = document.body.getAttribute('hx-headers')
const csrfToken = JSON.parse(hxHeaders)['X-CSRFToken'];
presignFileForUpload(file, signingEndpoint, csrfToken)
}

function resetForm(){
const uploadForm = document.getElementById('upload-form')
uploadForm.reset()
}

function handleS3Upload(file, data){
const fileName = data.filename ? data.filename : file.name
const newFileObject = new File([file], fileName, {type: file.type})
console.log("ready for s3", data, newFileObject)
resetForm()

}
function presignFileForUpload(file, endpoint, csrfToken) {
const fileName = file.name
const formData = new FormData()
formData.append("file_name", fileName)
fetch(endpoint,
{
method: "POST",
body: formData,
headers: {
"X-CSRFTOKEN": csrfToken,
}
}).then(response=>response.json())
.then(data=>{
handleS3Upload(file, data)
}).catch(err=>console.log('err', err))
}
</script>
{% endblock content %}

0 comments on commit 76bb480

Please sign in to comment.