Code changes:
Download cropperjs library.
-
Timestamp: 1:40
-
Find on GitHub: cropperjs library
-
On GitHub, click on "Code" (Green button) -> Download ZIP: to download the ZIP file.
-
Extract the ZIP folder
cropperjs-master.zip
we downloaded.
Create a account/templates/account
folder.
-
Timestamp: 2:20
-
Find on GitHub: static/cropperjs
-
Copy all the files/folders in
cropperjs-master
folder except the.github
folder and paste them intostatic/cropperjs
Collect Static.
Collects the static files into STATIC_ROOT
. Our STATIC_ROOT
is os.path.join(BASE_DIR, 'static_cdn')
-
Timestamp: 2:31
-
Inside
(venv) D:\DjangoProjects\ChatServerPlayground\venv\src
-
Run:
python manage.py collectstatic
-
Optional: If it asks for confirmation, type
yes
and hit Enter. -
Output: XXX static files copied to
D:\DjangoProjects\ChatServerPlayground\venv\src\static_cdn
. Open thestatic_cdn
folder locally and you will see files/folders copied there as shown in the video.
-
Install opencv-python.
Collects the static files into STATIC_ROOT
. Our STATIC_ROOT
is os.path.join(BASE_DIR, 'static_cdn')
-
Timestamp: 3:50
-
Inside
(venv) D:\DjangoProjects\ChatServerPlayground\venv\src
-
Run:
pip install opencv-python
-
Run:
pip freeze > requirements.txt
to record an environment's current package list into requirements.txt.
-
Update account/views.py
file.
-
Timestamp: 6:50
# TODO: ADD THESE IMPORTS TO THE TOP OF THE FILE.
from django.core.files.storage import default_storage
from django.core.files.storage import FileSystemStorage
import os
import cv2
import json
import base64
import requests
from django.core import files
ADD THESE LINE IMMEDIATELY BELOW THE IMPORTS.
TEMP_PROFILE_IMAGE_NAME = "temp_profile_image.png"
# TODO: ADD THESE METHODS.
def save_temp_profile_image_from_base64String(imageString, user):
INCORRECT_PADDING_EXCEPTION = "Incorrect padding"
try:
if not os.path.exists(settings.TEMP):
os.mkdir(settings.TEMP)
if not os.path.exists(settings.TEMP + "/" + str(user.pk)):
os.mkdir(settings.TEMP + "/" + str(user.pk))
url = os.path.join(settings.TEMP + "/" + str(user.pk),TEMP_PROFILE_IMAGE_NAME)
storage = FileSystemStorage(location=url)
image = base64.b64decode(imageString)
with storage.open('', 'wb+') as destination:
destination.write(image)
destination.close()
return url
except Exception as e:
print("exception: " + str(e))
# workaround for an issue I found
if str(e) == INCORRECT_PADDING_EXCEPTION:
imageString += "=" * ((4 - len(imageString) % 4) % 4)
return save_temp_profile_image_from_base64String(imageString, user)
return None
def crop_image(request, *args, **kwargs):
payload = {}
user = request.user
if request.POST and user.is_authenticated:
try:
imageString = request.POST.get("image")
url = save_temp_profile_image_from_base64String(imageString, user)
img = cv2.imread(url)
cropX = int(float(str(request.POST.get("cropX"))))
cropY = int(float(str(request.POST.get("cropY"))))
cropWidth = int(float(str(request.POST.get("cropWidth"))))
cropHeight = int(float(str(request.POST.get("cropHeight"))))
if cropX < 0:
cropX = 0
if cropY < 0: # There is a bug with cropperjs. y can be negative.
cropY = 0
crop_img = img[cropY:cropY+cropHeight, cropX:cropX+cropWidth]
cv2.imwrite(url, crop_img)
# delete the old image
user.profile_image.delete()
# Save the cropped image to user model
user.profile_image.save("profile_image.png", files.File(open(url, 'rb')))
user.save()
payload['result'] = "success"
payload['cropped_profile_image'] = user.profile_image.url
# delete temp file
os.remove(url)
except Exception as e:
print("exception: " + str(e))
payload['result'] = "error"
payload['exception'] = str(e)
return HttpResponse(json.dumps(payload), content_type="application/json")
Update account/urls.py
file.
-
Timestamp: 23:00
from account.views import (
account_view,
edit_account_view,
crop_image, # TODO: ADD THiS LINE.
)
app_name = 'account'
urlpatterns = [
path('<user_id>/', account_view, name="view"),
path('<user_id>/edit/', edit_account_view, name="edit"),
path('<user_id>/edit/cropImage/', crop_image, name="crop_image"), # TODO: ADD THiS LINE.
]
Update templates/base.html
file.
-
Timestamp: 23:40
#<!-- TODO: ADD THIS LINE. -->
<!-- Cropperjs -->
<link rel="stylesheet" href="{% static 'cropperjs/dist/cropper.min.css' %}">
// TODO: ADD THESE SCRIPTS.
<script type="module" src="{% static 'cropperjs/dist/cropper.min.js' %}"></script>
<script type="text/javascript">
function displayLoadingSpinner(isDisplayed){
var spinner = document.getElementById("id_loading_spinner")
if(isDisplayed){
spinner.style.display = "block"
}
else{
spinner.style.display = "none"
}
}
</script>
<!-- TODO: ADD THIS CLASS. -->
<div class="container" id="id_loading_spinner" style="display: none">
<div class="d-flex flex-row mx-auto flex-grow-1 justify-content-center">
<div class="spinner-border text-primary" role="status">
<span class="sr-only">Loading...</span>
</div>
</div>
</div>
Update account/templates/account/edit_account.html
file.
-
Timestamp: 27:25
-
Copy this entire file: edit_account.html
-
Mitch mentions about Base64.
Author
