Suneditor: Feature request: Allow the possibility to modify image blob/fileList prior to upload

Created on 21 May 2020  路  22Comments  路  Source: JiHong88/SunEditor

It would be useful to be able to perform file size reduction of images prior to upload. If a callback could be defined which is called at the very beginning of image dialog, the fileList/blob could be modified by this callback function and returned to the image dialog plugin for further processing and upload. How the callback could look like I can post an example here if/when this is implemented.

discussion enhancement

Most helpful comment

Example:

function ResizeImage (files, uploadHandler) {
    const uploadFile = files[0];
    const img = document.createElement('img');
    const canvas = document.createElement('canvas');
    const reader = new FileReader();

    reader.onload = function (e) {
        img.src = e.target.result
        img.onload = function () {
            let ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0);

            const MAX_WIDTH = 200;
            const MAX_HEIGHT = 100;
            let width = img.width;
            let height = img.height;

            if (width > height) {
                if (width > MAX_WIDTH) {
                    height *= MAX_WIDTH / width;
                    width = MAX_WIDTH;
                }
            } else {
                if (height > MAX_HEIGHT) {
                    width *= MAX_HEIGHT / height;
                    height = MAX_HEIGHT;
                }
            }

            canvas.width = width;
            canvas.height = height;

            ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, width, height);

            canvas.toBlob(function (blob) {
                uploadHandler([new File([blob], uploadFile.name)])
            }, uploadFile.type, 1);
        }
    }

    reader.readAsDataURL(uploadFile);
}

editor.onImageUploadBefore = function (files, info, core, uploadHandler) {
    console.log('files--', files);
    console.log('info--', info);
    ResizeImage(files, uploadHandler)

    // return undefined
}

All 22 comments

Hi, @MRB60
You can use the onImageUploadBefore event.

Ok thanks - not sure how to do that since I need to modify blob contents/resolution size and this function does not return new contents - changing function arguments I presume is not the way. I also believe is is called too late in the process for that looking at the image plugin code.
However, although it would be the preferred way since image upload size would then not be a limit anymore (file size reduced by browser before upload), I now handle it on the server side for the time being. This issue can be closed.

Currently, it seems difficult to adjust the file size before uploading.
How about letting onImageUploadBefore return a new file list?

Yes, it may work. File size checks are done prior to call and info prepared but not sure if this is a problem if the actual file blob (and w/h) is changed. I can make a try if onImageUploadBefore can return a new file list.

Ok, I will modify the code as below: image.js#L395

if (typeof this.functions.onImageUploadBefore === 'function') {
  files = this.functions.onImageUploadBefore(files, info, this);
  if (!files) return;
}

Code might become backward incompatible in case someone today returns true (would destroy files variable) so better make a warning in changelog. Or make an additional test on true before assigning "files".

The 2.30.0 version has been updated.
If this issue has not been resolved, please reopen this issue.
Thank you.

Currently it runs async.
Is resizing possible?

You are correct.
Blob generation needed for image resize runs async (e.g. "callback" needed) so it is not possible. I have an implemention in js but cannot use it with Suneditor today due to this async needs. I realized this late when looking into Suneditor src.

Promise is not supported in IE.
Did you have any idea?

In my solution, I will not support IE but blob generation is supported in IE (with own vendor prefix).
Blob generation is asynchronous and only requires a callback:
[]https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob
I do not think promise is mandatory.

@MRB60 @hongku
How about this format?

const callBack = function (handler) {
    const newFiles;
    handler(newFiles);
}

editor.uploadBefore = function (files, info, core, uploadHandler) {
    callBack(uploadHandler);
    return "await"
}

What does uploadhandler return?

"uploadhandler" is upload method of plugin. plugins.image.upload.bind(core, info)
A function that executed when "uploadBefore" returns true.

I did not use async/await to implement canvas to blob. I used a simple callback and thus I do not completely follow your suggestion on how to continue execution and make the upload of the new files.
See: []https://javascript.info/blob, there both callback method and async/await method is described.
Update:
if uploadHandler is the core function which takes care of the upload to server, I assume it should work. In the callback(), the blob creation(s) can be started and when completed, the uploadHandler is called with a new file list.

If undefined is returned from "uploadBefore", it waits until "uploadHandler" is executed.
"uploadHandler" is an upload function with "core" and "info" bound.

editor.uploadBefore = function (files, info, core, uploadHandler) {
  setTimeout(function() {
      // upload files
      uploadHandler(files /* or [new File(...)] */);
      // error
      uploadHandler('Error message');
      // Just finish with don't upload
      uploadHandler();
  },1000)
}

Example:

function ResizeImage (files, uploadHandler) {
    const uploadFile = files[0];
    const img = document.createElement('img');
    const canvas = document.createElement('canvas');
    const reader = new FileReader();

    reader.onload = function (e) {
        img.src = e.target.result
        img.onload = function () {
            let ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0);

            const MAX_WIDTH = 200;
            const MAX_HEIGHT = 100;
            let width = img.width;
            let height = img.height;

            if (width > height) {
                if (width > MAX_WIDTH) {
                    height *= MAX_WIDTH / width;
                    width = MAX_WIDTH;
                }
            } else {
                if (height > MAX_HEIGHT) {
                    width *= MAX_HEIGHT / height;
                    height = MAX_HEIGHT;
                }
            }

            canvas.width = width;
            canvas.height = height;

            ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, width, height);

            canvas.toBlob(function (blob) {
                uploadHandler([new File([blob], uploadFile.name)])
            }, uploadFile.type, 1);
        }
    }

    reader.readAsDataURL(uploadFile);
}

editor.onImageUploadBefore = function (files, info, core, uploadHandler) {
    console.log('files--', files);
    console.log('info--', info);
    ResizeImage(files, uploadHandler)

    // return undefined
}

The 2.30.1 version has been updated.
If this issue has not been resolved, please reopen this issue.
Thank you.

Tried your example but am getting uploadHandler not a function error

my working example:
after create:

    suneditor.onImageUploadBefore = function (files, info, core, uploadHandler) {
        ResizeImage(files, uploadHandler)
       }

and ResizeImage:

function ResizeImage (files, uploadHandler) {
    const uploadFile = files[0];
    const img = document.createElement('img');
    const canvas = document.createElement('canvas');
    const reader = new FileReader();

    reader.onload = function (e) {
        img.src = e.target.result
        img.onload = function () {
            let ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0);

            const MAX_WIDTH = IMMAXWIDTH;
            const MAX_HEIGHT = IMMAXHEIGHT;
            let width = img.width;
            let height = img.height;

            if (width > height) {
                if (width > MAX_WIDTH) {
                    height *= MAX_WIDTH / width;
                    width = MAX_WIDTH;
                }
            } else {
                if (height > MAX_HEIGHT) {
                    width *= MAX_HEIGHT / height;
                    height = MAX_HEIGHT;
                }
            }

            canvas.width = width;
            canvas.height = height;

            ctx = canvas.getContext("2d");
            ctx.drawImage(img, 0, 0, width, height);

            canvas.toBlob(function (blob) {
                uploadHandler([new File([blob], uploadFile.name)])
            }, uploadFile.type);
        }
    }
    reader.readAsDataURL(uploadFile);
}

Worked with Suneditor v2.31.0

ok, thanks

One more thing. How do I implement this in react, the uploadHandler is not recognised as a function.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

drnkwati picture drnkwati  路  6Comments

David-Grass-Feria picture David-Grass-Feria  路  6Comments

chagonzles picture chagonzles  路  4Comments

setesolutions picture setesolutions  路  7Comments

dreamsight picture dreamsight  路  5Comments