Braft Editor - Custom Media Upload and Validation

Braft Editor - Custom Media Upload and Validation

Braft Editor - Custom Media Upload Function

Introduction

In this article I am going to show you how to implement custom upload and validation function for media upload in Braft Editor.

Braft Editor is a web rich text editor based on draft-js, suitable for React framework, compatible with mainstream modern browsers.

The Problem

I am using Braft Editor for a project and I realised I am facing performance issue in website when I am adding content via Braft Editor. After hours of debugging I found that when images are added in Editor they are converted to PNG and it size also increases.

For example when a 32kb JPG Image was added to got converted to PNG and file size increase to 395 KB.

issue-demo-min.gif

Code Causing this issue

The code for media upload is in GitHub repository https://github.com/margox/braft-finder

Now one problem with Braft Editor documentation is ,its in Chinese and a non Chinese developer is at the mercy to Google Translate to understand the documentation.

So after not finding any solution in docs, I had to dig into source code for docs. And I found that the problem is caused because of below line of code.

https://github.com/margox/braft-finder/blob/master/src/utils/image.js

export const compressImage = (url, width = 1280, height = 800) => {

  return new Promise((resolve, reject) => {

    const image = new Image()

    image.src = url

    image.onerror = function (error) {
      reject(error)
    }

    image.onload = function () {

      try {

        const compressCanvas = document.createElement('canvas')
        const scale = (this.width > width || this.height > height) ? (this.width > this.height ? width / this.width : height / this.height) : 1

        compressCanvas.width = this.width * scale
        compressCanvas.height = this.height * scale

        const canvasContext = compressCanvas.getContext('2d')
        canvasContext.drawImage(this, 0, 0, compressCanvas.width, compressCanvas.height)

        resolve({
          url: compressCanvas.toDataURL('image/png', 1),
          width: compressCanvas.width,
          height: compressCanvas.height
        })

      } catch (error) {
        reject(error)
      }

    }

  })

}

Solution

As a naive react developer , I had two approaches

  • Somehow override Compress Image Function
  • Pass a custom upload function as props.

Going through docs , I found there is way to implement custom file upload component using antd .But its not what I wanted

I again had to dig in to , source code , where I found we can pass props to Braft Editor for custom upload and validation.

Code

Now lets dive into some code.

Custom Validation Function

I only wanted user to upload a file less than 100 kb ,So I implemented a custom validation function like below

  const validateFn = file => {
    let fileSizeError = "File Should be less than 100 kb";

    if (file.size > maxFileSize) {
      message.warn(fileSizeError);
      return false;
    }
  };

File Upload

In this demo , Custom file upload function converts the image to base64 whithout changing resolution and size. We can also upload this image to server or s3 bucket.

What's important here is once upload is successful in success call-back function we should return url of the image uploaded

File upload function in this demo

  const toBase64 = file =>
    new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = error => reject(error);
    });

  const customUpload = props => {
    const { file, success, error } = props;
    toBase64(file)
      .then(res => {
        success({ url: res });
      })
      .catch(err => {
        message.warn(" File upload failed");
        error(err.message);
      });
  };

Sample code to upload file to server

 const customUpload = props => {
    const { file, success, error } = props;
    let formData = new FormData();
    formData.append("files", file);

    fetch('/upload', {
      method: 'POST',
      body: formData
    }).then(res=>{
        success({url:res});
    }).catch(err=>{
        error(err.message);
    }) 
  };

Final Code

import React from "react";
import "./styles.css";
import "braft-editor/dist/index.css";
import BraftEditor from "braft-editor";
import { message } from "antd";

const maxFileSize = 100000; //100 kb

export default function App() {
  const controls = [
    "bold",
    "italic",
    "underline",
    "separator",
    "link",
    "separator",
    "media"
  ];

  const toBase64 = file =>
    new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = error => reject(error);
    });

  const customUpload = props => {
    const { file, success, error } = props;
    toBase64(file)
      .then(res => {
        success({ url: res });
      })
      .catch(err => {
        message.warn(" File upload failed");
        error(err.message);
      });
  };

  const validateFn = file => {
    let fileSizeError = "File Should be less than 100 kb";

    if (file.size > maxFileSize) {
      message.warn(fileSizeError);
      return false;
    }
  };

  return (
    <div className="App">
      <h1>Braft Editor - Custom Media Upload Demo</h1>
      <div className="editor-wrapper">
        <BraftEditor
          language="en"
          controls={controls}
          media={{ uploadFn: customUpload, validateFn: validateFn }}
          contentStyle={{
            height: 210,
            boxShadow: "inset 0 1px 3px rgba(0,0,0,.1)"
          }}
        />
      </div>
    </div>
  );
}

Codesandbox Demo

Edit

Conclusion

Thanks for reading! Follow me on Github and Instagram if you want to keep in touch.