kartik-v / bootstrap-fileinput

An enhanced HTML 5 file input for Bootstrap 5.x/4.x./3.x with file preview, multiple selection, and more features.
http://plugins.krajee.com/file-input
Other
5.36k stars 2.4k forks source link

[Help] Crop Image #865

Open cristinaITdeveloper opened 7 years ago

cristinaITdeveloper commented 7 years ago

How can I crop the image before the upload? Thanks for your help!

kartik-v commented 7 years ago

You can read about plugin events in documentation and use events like fileimageloaded or fileimagesloaded or even after the file is uploaded using events like fileuploaded. Within these event callbacks, you can call your image cropping plugin library to update the preview image and update via an ajax command. Alternatively - for advanced cases, you can overwrite the filestack to be uploaded itself by clearing it with clearFileStack method.and converting each image into blob and call addToStack method to update the filestack.

dcy commented 7 years ago

@cristinaITdeveloper Have you done Crop Image with bootstrap-fileinput?

dcy commented 7 years ago

@kartik-v Can you write a demo for Crop Image?

erenmustafaozdal commented 6 years ago

Hi @dcy . This may be a basic example for you:

// HTML
<input type="file" name="photo">

// JS
$('input[name="photo"]').fileinput({
    // fileinput options
}).on('fileimageloaded', function(event, previewId) {
    $('#' + previewId).find('img.file-preview-image').JCrop({
        // jcrop options
    });
})
lestcape commented 1 year ago

This is what i do about this issue, but i have some problems yet.

I'm using the bootstrap-fileinput version 5.0.8 and i only want to upload one images at time. My idea was add a button to the .file-footer-buttons div class and use that button to cut the image on click. The library I'm using to cut the image is the no longer maintained version of cropper https://github.com/fengyuanchen/cropper. The crop of the image is apply directly over the .file-preview-image of the bootstrap-fileinput and the mentioned button accept the crop and try to prepare the bootstrap-fileinput for upload the image.

Also please note: The updateStack method is now deprecated, so i' m trying to use the addToStack method instead.

The problem i have is that addToStack don't seem to work and the only way i have to resolve the situation is inject the file directly inside the fileinput dom object by myself.

I want to delegate the file to the fileinput and not do it by myself. So please @kartik-v, can you help me to detected what i' m doing wrong here? Thanks anyway.

var instance = el.fileinput(options);

 //TODO: https://github.com/kartik-v/bootstrap-fileinput/issues/865
var imageCropping= el.data("image-cropping");
if (imageCropping && $.fn.cropper) {//thumb-avatar-22544_avatar_491.png
    instance.on("fileimageloaded", function(event, previewId) {
        // The id is not recognize by jquery as is the name of the file.
        // So, should be better detect the element directly using javascript.
        var elem = document.getElementById(previewId);
        if (elem) {
            var img = $(elem).find("img.file-preview-image").first();
            //img max-width: 100%;
            img.cropper({
                aspectRatio: 1 / 1,
                crop: function(event) {
                    console.log(event.detail.x);
                    console.log(event.detail.y);
                    console.log(event.detail.width);
                    console.log(event.detail.height);
                    console.log(event.detail.rotate);
                    console.log(event.detail.scaleX);
                    console.log(event.detail.scaleY);
                 }
            });
            var bttns = $(elem).find("div.file-footer-buttons").first();
            if (bttns.length) {
                var btn = $("<button type='button' class='kv-file-zoom btn btn-sm btn-kv btn-default btn-outline-secondary'"+
                            " title='Cortar la imagen'><span class='glyphicon glyphicon-scissors'></span></button>");
                btn.on("click", function(event) {
                    var cropper = img.data("cropper");
                    var cropperImg = cropper.getCroppedCanvas().toDataURL("image/png");                            
                    //img.attr("src", cropperImg);
                    img.removeClass("cropper-hidden");
                    img.siblings(".cropper-container").addClass("cropper-hidden");
                    // https://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
                    fetch(cropperImg).then(function(res) {
                        res.blob().then(function(blobFile) {
                            // Update the image preview here:
                            var blobUrl = URL.createObjectURL(blobFile);
                            img.attr("src", blobUrl);

                             // Clear the FileStack here (but it really have nothing??????):
                            el.fileinput("clearFileStack");
                            /*var filestack = el.fileinput("getFileStack")
                            $.each(filestack, function(fileId, fileObj) {
                                if (fileObj !== undefined) {
                                    alert(fileObj.length);
                                }
                            });*/

                            // Add the new image to the FileStack (But that not work at all????)
                            el.fileinput("addToStack", blobFile, "idFile");

                            // So, manual set the file to the input (this works, but is not what i want)
                            var name = (el[0].files.length) ? el[0].files[0].name : "img.png";
                            var file = new File([blobFile], name, { type: "image/png", lastModified: new Date().getTime() });
                            var container = new DataTransfer();
                            container.items.add(file);
                            el[0].files = container.files;
                            el[0].dispatchEvent(new Event("change", { "bubbles": true }));
                        });
                   });
                });
                bttns.append(btn);
            }
        }
    });
}
kartik-v commented 1 year ago

@lestcape the issue in your case looks like that you are trying to USE FORM BASED submission and not the AJAX MODE of submission - in that case addToStack method will not have any impact. If you use the AJAX mode of submission - then the files will be uploaded through a customized file management stack as part of the plugin feature. If you use plain FORM BASED submission - in that case files will be read through the NATIVE file input - in that case whatever you do you cannot edit files in the native input through external script (standard HTML /JS browser security protocols)

kartik-v commented 1 year ago

BTW - I am noting a separate enhancement to reintroduce updateStack method to update the stack (which will work only for ajax uploads).

lestcape commented 1 year ago

in that case files will be read through the NATIVE file input - in that case whatever you do you cannot edit files in the native input through external script (standard HTML /JS browser security protocols)

Sorry, but this is a false assumption. You can not read a file directly in the client file system, because the browser security protocols, but if you have already the file some how (you created it in your script or the user already give it to you) you can send it to the server using a NATIVE file input. This is what this code is doing and it works:

var name = (el[0].files.length) ? el[0].files[0].name : "img.png";
var file = new File([blobFile], name, { type: "image/png", lastModified: new Date().getTime() });
var container = new DataTransfer();
container.items.add(file);
el[0].files = container.files;
el[0].dispatchEvent(new Event("change", { "bubbles": true }));

So, i assumed then that you should also allow that in your plugins, not just for ajax but in general and then hide the complexity of the back-end method to the user.

In any case, you answered my question... Your plugin have not a custom way to upload files using a native file input, because YOU considered this a security limitation, while is really not that way in practice.

lestcape commented 1 year ago

Only to add that the code to upload files using a native file input is tested by me on all that platforms and it working:

Firefox 107 Linux (Ubuntu 22.04) Chrome 107.0.5304.110 Linux (Ubuntu 22.04) Firefox 107 Windows 11 Chrome 107.0.5304.107 Windows 11 Safari 14.1.2 Mac OS Mojave Edge 107.0.1418.35 Windows 10 Samsung Browser 19.0.1.2 On Android 12 (Galaxy S21 Ultra). Chrome 107.0.5304.105 On Android 12 (Galaxy S21 Ultra).

On Safari 14.1.2 Mac OS Mojave the fileinput plugins don't work properly. For an unknown reason to me, instead of update the current preview image it duplicate the preview, but the uploads of the file using the native inputs works!!

I have not an Applet device with IOS to test it there and i don't know exactly in what minimum version of what browser it will works, but i think this is enough to confirm that is possible in almost all current browser.

kartik-v commented 1 year ago

@lestcape - understand and thanks for the update. As mentioned as of now you cannot use the plugin methods like addToStack if you are reading files from native file input. However I am noting an enhancement for potential functionality to update an already selected file as you pointed out for NATIVE INPUT usage. Note that this will only work when you have a blobFile available to you already.

lestcape commented 1 year ago

Note that this will only work when you have a blobFile available to you already.

Ofcourse!! addToStack recive a blob file as a parameter. So, how can be this differently?

I am noting an enhancement for potential functionality to update an already selected file as you pointed out for NATIVE INPUT usage

Yes that is the point. This is what is supposed that should occurs transparently... If you are using ajax it should works if you are using a native file input it should work too. There are not any reason to not works.

As mentioned as of now you cannot use the plugin methods like addToStack if you are reading files from native file input.

Yes, sure, i know. The point is, that i think this should be possible.

kartik-v commented 1 year ago

Yes, sure, i know. The point is, that i think this should be possible.

@lestcape - yes the support for DataTransfer api to create a filelist that can be assigned to native input was not consistently available across browsers (especially earlier versions and IE) ... and still some device browsers may not support it... but since it is now available in mainstream browsers ... it is a possible option for enhancement.

lestcape commented 1 year ago

Yes, that is a common scenario for a lot of browser features, but IE should be old history for most of people now and things should evolved. I think that you can check the availability of DataTransfer and if it's available use the stack otherwise throw a warning to the console and not use it. Additionally will be necessary documented the support for upload files with a native input and there specify that the support for older browser are only with the ajax method.

I live in Mexico and work for the Mexican government, so i understand perfectly why the support for old browser is needed to some peoples, but keep compatibility with really old browser should not be a barrier in most of scenarios.