Days
Hours
Minutes
Seconds
x

Froala Editor v4.2.0 is Here LEARN MORE

Skip to content

Django

Video Upload

The following code example illustrates how to handle video upload on your server using django as a server-side language. For step by step explanation of the upload flow see video upload concept.

Requirements

For this example to work you need to have an upload folder

To easily create the upload folder copy and paste this code snippet to your settings.py file

# Create public directory at startup.
publicDirectory = os.path.join(BASE_DIR, "public")
if not os.path.exists(publicDirectory):
os.makedirs(publicDirectory)

Frontend

The main index page.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">

  <!-- Include external CSS. -->
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet" type="text/css" />
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.0/codemirror.min.css">

  <!-- Include Editor style. -->
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/css/froala_style.min.css" rel="stylesheet" type="text/css" />
</head>

<body>
  <!-- Include external JS libs. -->
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.0/codemirror.min.js"></script>
  <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.25.0/mode/xml/xml.min.js"></script>

  <!-- Include Editor JS files. -->
  <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]//js/froala_editor.pkgd.min.js"></script>
  <div class="sample">
    <h2>File upload example.</h2>
    <form>
      <textarea id="edit" name="content"></textarea>
    </form>
  </div>

  <!-- Initialize the editor. -->
   <script>
     $(function() {
      $('#edit').froalaEditor({
        // Set the video upload URL.
        videoUploadURL: '/upload_video',
        videoUploadParams: {
          id: 'my_editor'
        },
      })
    });
  </script>
</body>
</html>

Backend

views.py file will handle the server part for django

Inside the views.py file is the Video class this will be a handle the video upload

from django.http import HttpResponse
from django.conf import settings
from django.shortcuts import render_to_response
from os.path import isfile, join
from mimetypes import MimeTypes
from os import listdir
from wand.image import Image
import wand.image
import hashlib
import json
import time
import hmac
import copy
import sys
import os

def index(request):
    return render_to_response(os.path.join(settings.STATIC_DIR, "index.html"))


def upload_video(request):
    try:
        response = Video.upload(DjangoAdapter(request), "/public/")
    except Exception:
        response = {"error": str(sys.exc_info()[1])}
    return HttpResponse(json.dumps(response), content_type="application/json")


def upload_video_validation(request):

    def validation(filePath, mimetype):
        size = os.path.getsize(filePath)
        if size > 50 * 1024 * 1024:
            return False
        return True

    options = {
        "fieldname": "myFile",
        "validation": validation
    }

    try:
        response = Video.upload(DjangoAdapter(request), "/public/", options)
    except Exception:
        response = {"error": str(sys.exc_info()[1])}
    return HttpResponse(json.dumps(response), content_type="application/json")


class Video(object):

    defaultUploadOptions = {
        "fieldname": "file",
        "validation": {
            "allowedExts": ["mp4", "webm", "ogg"],
            "allowedMimeTypes": ["video/mp4", "video/webm", "video/ogg"]
        }
    }

    @staticmethod
    def upload(req, fileRoute, options=None):
        """
        Video upload to disk.
        Parameters:
            req: framework adapter to http request. See BaseAdapter.
            fileRoute: string
            options: dict optional, see defaultUploadOptions attribute
        Return:
            dict: {link: "linkPath"}
        """

        if options is None:
            options = Video.defaultUploadOptions
        else:
            options = Utils.merge_dicts(Video.defaultUploadOptions, options)

        return File.upload(req, fileRoute, options)

    @staticmethod
    def delete(src):
        return File.delete(src)

    @staticmethod
    def list(folderPath, thumbPath=None):

        if thumbPath == None:
            thumbPath = folderPath

        # Array of video objects to return.
        response = []

        absoluteFolderPath = Utils.getServerPath() + folderPath

        # Video types.
        videoTypes = Video.defaultUploadOptions["validation"]["allowedMimeTypes"]

        # Filenames in the uploads folder.
        fnames = [f for f in listdir(absoluteFolderPath) if isfile(join(absoluteFolderPath, f))]

        for fname in fnames:
            mime = MimeTypes()
            mimeType = mime.guess_type(absoluteFolderPath + fname)[0]

            if mimeType in videoTypes:
                response.append({
                    "url": folderPath + fname,
                    "thumb": thumbPath + fname,
                    "name": fname
                })

        return response


class Utils(object):
    """
    Utils static class.
    """

    @staticmethod
    def hmac(key, string, hex=False):
        """
        Calculate hmac.
        Parameters:
            key: string
            string: string
            hex: boolean optional, return in hex, else return in binary
        Return:
            string: hmax in hex or binary
        """

        # python 2-3 compatible:
        try:
            hmac256 = hmac.new(key.encode() if isinstance(key, str) else key, msg=string.encode("utf-8") if isinstance(string, str) else string, digestmod=hashlib.sha256) # v3
        except Exception:
            hmac256 = hmac.new(key, msg=string, digestmod=hashlib.sha256) # v2

        return hmac256.hexdigest() if hex else hmac256.digest()

    @staticmethod
    def merge_dicts(a, b, path=None):
        """
        Deep merge two dicts without modifying them. Source: http://stackoverflow.com/questions/7204805/dictionaries-of-dictionaries-merge/7205107#7205107
        Parameters:
            a: dict
            b: dict
            path: list
        Return:
            dict: Deep merged dict.
        """

        aClone = copy.deepcopy(a);
        # Returns deep b into a without affecting the sources.
        if path is None: path = []
        for key in b:
            if key in a:
                if isinstance(a[key], dict) and isinstance(b[key], dict):
                    aClone[key] = Utils.merge_dicts(a[key], b[key], path + [str(key)])
                else:
                    aClone[key] = b[key]
            else:
                aClone[key] = b[key]
        return aClone

    @staticmethod
    def getExtension(filename):
        """
        Get filename extension.
        Parameters:
            filename: string
        Return:
            string: The extension without the dot.
        """
        return os.path.splitext(filename)[1][1:]

    @staticmethod
    def getServerPath():
        """
        Get the path where the server has started.
        Return:
            string: serverPath
        """
        return os.path.abspath(os.path.dirname(sys.argv[0]))

    @staticmethod
    def isFileValid(filename, mimetype, allowedExts, allowedMimeTypes):
        """
        Test if a file is valid based on its extension and mime type.
        Parameters:
            filename string
            mimeType string
            allowedExts list
            allowedMimeTypes list
        Return:
            boolean
        """

        # Skip if the allowed extensions or mime types are missing.
        if not allowedExts or not allowedMimeTypes:
            return False

        extension = Utils.getExtension(filename)
        return extension.lower() in allowedExts and mimetype in allowedMimeTypes

    @staticmethod
    def isValid(validation, filePath, mimetype):
        """
        Generic file validation.
        Parameters:
            validation: dict or function
            filePath: string
            mimetype: string
        """

        # No validation means you dont want to validate, so return affirmative.
        if not validation:
            return True

        # Validation is a function provided by the user.
        if callable(validation):
            return validation(filePath, mimetype)

        if isinstance(validation, dict):
            return Utils.isFileValid(filePath, mimetype, validation["allowedExts"], validation["allowedMimeTypes"])

        # Else: no specific validating behaviour found.
        return False


class BaseAdapter(object):
    """
    Interface. Inherit this class to use the lib in your framework.
    """

    def __init__(self, request):
        """
        Constructor.
        Parameters:
            request: http request object from some framework.
        """
        self.request = request

    def riseError(self):
        """
        Use this when you want to make an abstract method.
        """
        raise NotImplementedError( "Should have implemented this method." )

    def getFilename(self, fieldname):
        """
        Get upload filename based on the fieldname.
        Parameters:
            fieldname: string
        Return:
            string: filename
        """
        self.riseError()

    def getMimetype(self, fieldname):
        """
        Get upload file mime type based on the fieldname.
        Parameters:
            fieldname: string
        Return:
            string: mimetype
        """
        self.riseError()

    def saveFile(self, fieldname, fullNamePath):
        """
        Save the upload file based on the fieldname on the fullNamePath location.
        Parameters:
            fieldname: string
            fullNamePath: string
        """
        self.riseError()


class DjangoAdapter(BaseAdapter):
    """
    Django Adapter: Check BaseAdapter to see what methods description.
    """

    def checkFile(self, fieldname):
        if fieldname not in self.request.FILES:
            raise Exception("File does not exist.")

    def getFilename(self, fieldname):
        self.checkFile(fieldname)
        return self.request.FILES[fieldname].name

    def getMimetype(self, fieldname):
        self.checkFile(fieldname)
        return self.request.FILES[fieldname].content_type

    def saveFile(self, fieldname, fullNamePath):
        print("should save now")
        print("the path" + fullNamePath)
        self.checkFile(fieldname)

        with open(fullNamePath, "wb+") as destination:
            for chunk in self.request.FILES[fieldname].chunks():
                destination.write(chunk)

In urls.py file are defined the request path with the according method, in this case the video upload

from django.conf.urls import url
import views
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
    url(r"^$", views.index, name="index"),
    url(r"^upload_video$", views.upload_video, name="upload_video"),
    url(r"^upload_video_validation", views.upload_video_validation, name="upload_video_validation")
] + static(settings.STATIC_URL, document_root=settings.STATIC_DIR) + static(settings.STATIC_PUBLIC_URL, document_root=settings.STATIC_PUBLIC_DIR)
[class^="wpforms-"]
[class^="wpforms-"]
[bws_google_captcha]
<div class="gglcptch gglcptch_v2"><div id="gglcptch_recaptcha_1848202834" class="gglcptch_recaptcha"></div> <noscript> <div style="width: 302px;"> <div style="width: 302px; height: 422px; position: relative;"> <div style="width: 302px; height: 422px; position: absolute;"> <iframe src="https://www.google.com/recaptcha/api/fallback?k=6Ld6lNoUAAAAAM626LfCOrnkBFJtYZAKESFCjgv_" frameborder="0" scrolling="no" style="width: 302px; height:422px; border-style: none;"></iframe> </div> </div> <div style="border-style: none; bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px; background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px; height: 60px; width: 300px;"> <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px !important; height: 40px !important; border: 1px solid #c1c1c1 !important; margin: 10px 25px !important; padding: 0px !important; resize: none !important;"></textarea> </div> </div> </noscript></div>
[class^="wpforms-"]
[class^="wpforms-"]
[bws_google_captcha]
<div class="gglcptch gglcptch_v2"><div id="gglcptch_recaptcha_1691496058" class="gglcptch_recaptcha"></div> <noscript> <div style="width: 302px;"> <div style="width: 302px; height: 422px; position: relative;"> <div style="width: 302px; height: 422px; position: absolute;"> <iframe src="https://www.google.com/recaptcha/api/fallback?k=6Ld6lNoUAAAAAM626LfCOrnkBFJtYZAKESFCjgv_" frameborder="0" scrolling="no" style="width: 302px; height:422px; border-style: none;"></iframe> </div> </div> <div style="border-style: none; bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px; background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px; height: 60px; width: 300px;"> <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px !important; height: 40px !important; border: 1px solid #c1c1c1 !important; margin: 10px 25px !important; padding: 0px !important; resize: none !important;"></textarea> </div> </div> </noscript></div>
[class^="wpforms-"]
[class^="wpforms-"]
[bws_google_captcha]
<div class="gglcptch gglcptch_v2"><div id="gglcptch_recaptcha_686490096" class="gglcptch_recaptcha"></div> <noscript> <div style="width: 302px;"> <div style="width: 302px; height: 422px; position: relative;"> <div style="width: 302px; height: 422px; position: absolute;"> <iframe src="https://www.google.com/recaptcha/api/fallback?k=6Ld6lNoUAAAAAM626LfCOrnkBFJtYZAKESFCjgv_" frameborder="0" scrolling="no" style="width: 302px; height:422px; border-style: none;"></iframe> </div> </div> <div style="border-style: none; bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px; background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px; height: 60px; width: 300px;"> <textarea id="g-recaptcha-response" name="g-recaptcha-response" class="g-recaptcha-response" style="width: 250px !important; height: 40px !important; border: 1px solid #c1c1c1 !important; margin: 10px 25px !important; padding: 0px !important; resize: none !important;"></textarea> </div> </div> </noscript></div>