Days
Hours
Minutes
Seconds
x

Froala Editor v4.2.0 is Here LEARN MORE

Skip to content

Django

File Upload

The following sections describe how to handle file uploads on your server using Django as a server-side language. For information on the upload workflow refer to the file upload documentation.

Frontend

Setting up the index page.

  1. On the head section include the Editor style.

  2. <!DOCTYPE html>
    <html>
    <head>
    <meta charset="utf-8">
    
    <link href="https://cdn.jsdelivr.net/npm/froala-editor@latest/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
    </head>
  3. On the body section include the Editor JS files and define the area for the editor.

  4. <body>  
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/froala-editor@latest/js/froala_editor.pkgd.min.js"></script>
    
    <div class="sample">
    <h2>File upload example.</h2>
    <form>
    <textarea id="edit" name="content"></textarea>
    </form>
    </div>
    
  5. Initialize the editor and set the file upload URL

  6. <script>
    new FroalaEditor('#edit', {
    // Set the file upload URL.
    fileUploadURL: '/UploadFiles',
    
    fileUploadParams: {
    id: 'my_editor'
    }
    })
    </script>
    </body>
    </html>

The full code should look like this:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/froala-editor@latest/css/froala_editor.pkgd.min.css" rel="stylesheet" type="text/css" />
</head>

<body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/froala-editor@latest/js/froala_editor.pkgd.min.js"></script>

<div class="sample">
<h2>File upload example.</h2>
<form>
<textarea id="edit" name="content"></textarea>
</form>
</div>


<script>
new FroalaEditor('#edit', {

fileUploadURL: '/UploadFiles',

fileUploadParams: {
id: 'my_editor'
}
})
</script>
</body>
</html>

Backend

views.py handles the server part for django

Inside views.py, the File class handles the file 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_file(request):
  options = {
  "validation": None
  }
  
  try:
  response = File.upload(DjangoAdapter(request), "/public/", options)
  except Exception:
  response = {"error": str(sys.exc_info()[1])}
  return HttpResponse(json.dumps(response), content_type="application/json")
  
  
  def upload_file_validation(request):
  
  def validation(filePath, mimetype):
  size = os.path.getsize(filePath)
  if size > 10 * 1024 * 1024:
    return False
  return True
  
  options = {
  "fieldname": "myFile",
  "validation": validation
  }
  
  try:
  response = File.upload(DjangoAdapter(request), "/public/", options)
  except Exception:
  response = {"error": str(sys.exc_info()[1])}
  return HttpResponse(json.dumps(response), content_type="application/json")
  
  
  class File(object):
  
  defaultUploadOptions = {
  "fieldname": "file",
  "validation": {
    "allowedExts": ["txt", "pdf", "doc"],
    "allowedMimeTypes": ["text/plain", "application/msword", "application/x-pdf", "application/pdf"]
  }
  }
  
      @staticmethod
  def upload(req, fileRoute, options = None):
  """
  File 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 = File.defaultUploadOptions
  else:
    options = Utils.merge_dicts(File.defaultUploadOptions, options)
  
  # Get extension.
  filename = req.getFilename(options["fieldname"]);
  extension = Utils.getExtension(filename)
  extension = "." + extension if extension else ""
  
  # Generate new random name.
  
  # python 2-3 compatible:
  try:
    sha1 = hashlib.sha1(str(time.time()).encode("utf-8")).hexdigest() #  v3
  except Exception:
    sha1 = hashlib.sha1(str(time.time())).hexdigest() # v2
  routeFilename = fileRoute + sha1 + extension
  
  fullNamePath = Utils.getServerPath() + routeFilename
  
  req.saveFile(options["fieldname"], fullNamePath)
  
  # Check validation.
  if "validation" in options:
    if not Utils.isValid(options["validation"], fullNamePath, req.getMimetype(options["fieldname"])):
        File.delete(routeFilename)
        raise Exception("File does not meet the validation.")
  
  # Check image resize.
  if "resize" in options and options["resize"] is not None:
    with Image(filename=fullNamePath) as img:
        img.transform(resize=options["resize"])
        img.save(filename=fullNamePath)
  
  # build and send response.
  response = {}
  response["link"] = routeFilename
  return response
  
      @staticmethod
  def delete(src):
  """
  Delete file from disk.
  Parameters:
    src: string
  """
  
  filePath = Utils.getServerPath() + src
  try:
    os.remove(filePath)
  except OSError:
    pass
  
  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 define the request path with the according method, in this case the file 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_file$", views.upload_file, name="upload_file"),
  url(r"^upload_file_validation", views.upload_file_validation, name="upload_file_validation"),
  ] + static(settings.STATIC_URL, document_root=settings.STATIC_DIR) + static(settings.STATIC_PUBLIC_URL, document_root=settings.STATIC_PUBLIC_DIR)
  

Do you think we can improve this article? Let us know.

Ready to dive in? Explore our plans