Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
MRB60 opened this issue May 21, 2020 · 22 comments

Comments

@MRB60
Copy link

MRB60 commented May 21, 2020

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.

@JiHong88
Copy link
Owner

Hi, @MRB60
You can use the onImageUploadBefore event.

@MRB60
Copy link
Author

MRB60 commented May 22, 2020

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.

@JiHong88
Copy link
Owner

JiHong88 commented May 23, 2020

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

@MRB60
Copy link
Author

MRB60 commented May 23, 2020

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.

@JiHong88
Copy link
Owner

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;
}

@MRB60
Copy link
Author

MRB60 commented May 23, 2020

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".

@JiHong88 JiHong88 added this to the 2.30.0 milestone May 23, 2020
@JiHong88
Copy link
Owner

JiHong88 commented Jun 9, 2020

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

@JiHong88
Copy link
Owner

Currently it runs async.
Is resizing possible?

@MRB60
Copy link
Author

MRB60 commented Jun 10, 2020

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.

@JiHong88
Copy link
Owner

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

@MRB60
Copy link
Author

MRB60 commented Jun 10, 2020

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.

@JiHong88 JiHong88 removed this from the 2.30.0 milestone Jun 10, 2020
@JiHong88
Copy link
Owner

@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"
}

@hongku
Copy link

hongku commented Jun 10, 2020

What does uploadhandler return?

@JiHong88
Copy link
Owner

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

@MRB60
Copy link
Author

MRB60 commented Jun 10, 2020

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.

@JiHong88 JiHong88 added this to the 2.30.1 milestone Jun 11, 2020
JiHong88 added a commit that referenced this issue Jun 15, 2020
@JiHong88
Copy link
Owner

JiHong88 commented Jun 15, 2020

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)
}

@JiHong88
Copy link
Owner

JiHong88 commented Jun 15, 2020

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
}

@JiHong88
Copy link
Owner

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

@sleekcharly
Copy link

Tried your example but am getting uploadHandler not a function error

@MRB60
Copy link
Author

MRB60 commented Jul 8, 2020

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

@sleekcharly
Copy link

ok, thanks

@sleekcharly
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants