scottcheng / cropit

A "customizable crop and zoom" jQuery plugin.
http://scottcheng.github.io/cropit/
MIT License
874 stars 304 forks source link

CORS errors in Chrome with console closed #118

Closed aas395 closed 8 years ago

aas395 commented 8 years ago

Hi,

The new version seems to fix a lot of things (while breaking others). For my own use case we're most of the way there, but I'm running into some CORS issues now on Chrome.

With the console open, everything appears to work as you'd expect. Only once the console is closed do things go awry. If the image you're using by default in the imageState.src parameter is on AWS S3, I get CORS errors in Mac Chrome 46.0.2490.80 (64-bit).

I dug into this a bit and it looks like there are a few potential issues:

1) The "allowCrossOrigin" option isn't checked everywhere where images are being pulled from external sources. There is no check for this in onImageLoaded() before this is done, which is part of the reason I'm getting the error:

this.$imageBg.attr('src', this.imageSrc);

2) There are up to 4 requests made for the image you list in your imageState.src parameter. I understand why two of those are necessary (the original request and the data URI you add as a background image in the image preview) but the rest don't seem to be necessary and offer more opportunities to break the rules we laid out in allowCrossOrigin.

3) When the background image of the image preview is being set in onImageLoaded(), the browser makes another request (this time through the browser's CSS stuff, which gives us no control over headers) which appears to be the source of the CORS issue on Chrome once the other crossOrigin issues are neutralized. Ideally this would just be the data URI generated from the canvas to avoid any potential CORS issues. When I make this change it fixes the issue for me, although I'm still working on getting the preview image to be the appropriate size with this method.

Any chance you can help me with this sometime soon?

aas395 commented 8 years ago

Confirmed the two steps to fix this:

1) Add the crossOrigin attribute for crossOrigin-allowed requests above line 416 2) Use a canvas to convert the loaded image into a data URI and insert the data URI instead of the source image's URL above line 414. Don't use getCroppedImage() because we need the original image for the preview, not the cropped one.

Let me know if you have any questions.

aas395 commented 8 years ago

Here's what fixed both these problems and the problems in #120. Added this inside loadImage() at line 373:

          //make this a blob url to start to avoid cross origin problems
      if (this.options.allowCrossOrigin && imageSrc.indexOf('data:') !== 0) {

        var xhr = new XMLHttpRequest();
        var cropit = this;

        xhr.onload = function () {
            // window.URL = window.URL || window.webkitURL;
            var url = URL.createObjectURL(this.response), img = new Image();

            img.onload = function () {
                // here you can use img for drawing to canvas and handling
                // ...
                canvas = document.createElement('canvas');
                canvas.width = img.width; // or 'width' if you want a special/scaled size
                canvas.height = img.height; // or 'height' if you want a special/scaled size

                context = canvas.getContext('2d');
                context.fillStyle = {
                    type: 'image/png',
                    quality: 1,
                    originalSize: false,
                    fillBg: '#fff'
                };

                context.drawImage(img, 0, 0, canvas.width, canvas.height);
                var imageData = canvas.toDataURL("data:image/png");

                // don't forget to free memory up when you're done (you can do this as soon as image is drawn to canvas)
                URL.revokeObjectURL(url);
                cropit.preImage.src = imageData;
            };

            img.src = url;
        };

        xhr.open('GET', imageSrc, true);
        xhr.responseType = 'blob';
        xhr.send();

      } else {//handle this the usual way
        this.preImage.src = imageSrc;
      }

The idea is basically that there are a number of ajax requests that are made throughout the lifecycle of cropping, and each opens up an opportunity for a CORS error. IE10 and 11 (at least the versions I was using) didn't respond to setting the image's crossOrigin property to 'anonymous', so to work around that you have to get the image file via ajax, convert the file to a data uri, and then work with that URI for the rest of the cropping lifecycle.

I actually think this is cleaner than what's currently being done, since it greatly reduces the potential number of requests that need to be made and completely eliminates the possibility of a CORS issue.

I'm sure there are issues with what I've written (my goal right now is to get this working, above all else) but this basic approach solves all the issues I've been seeing.

hossein761 commented 8 years ago

This fix doesn't seem to be released or merged with the original source. any plans for it? This is blocker issue for us! Thanks

mohsincynexis commented 8 years ago

I used your hack, but it does not seems to be working. Can you guys let me know what could be the issue.

What I am doing is calling ajax to load bootstrap modal that contains the crop container and then appends it to the body. And then trying to set the src with an external url but getting this error XMLHttpRequest cannot load http://s1.picswalls.com/wallpapers/2014/12/09/butterfly-wallpaper_093549561_256.jpeg. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://mohsin-pc' is therefore not allowed access. Can you guys help me fixing this issue.

Thanks, Mohsin

aas395 commented 8 years ago

That doesn't sound like an issue with the plugin... Do you own http://s1.picswalls.com/? Unless that Access-Control-Allow-Origin header is in the response from that server, I don't think you'll be able to interact with that image via AJAX. Here's more info:

http://stackoverflow.com/questions/20035101/no-access-control-allow-origin-header-is-present-on-the-requested-resource

mohsincynexis commented 8 years ago

So there is no way I can display external images if I use ajax.

mohsincynexis commented 8 years ago

My Main purpose is allow user to enter image URL to scale and crop images. So if I set the image src using $('.image-editor').cropit('imageSrc', 'http://s1.picswalls.com/wallpapers/2014/12/09/butterfly-wallpaper_093549561_256.jpeg');

It does not works. what could be the possible solution to do this.

aas395 commented 8 years ago

You can't do what you're trying to do if the image response doesn't have an appropriate Access-Control-Allow-Origin header that allows your host. This is a browser error, not a plugin error. Read about CORS and see if that doesn't help explain what's going on here.

mohsincynexis commented 8 years ago

Sorry for being so stupid, what if I do not use ajax. But dynamically setting the crop src when some enter the image URL in the text box does not seems to be working too.

aas395 commented 8 years ago

Not stupid, but you could have discovered what this error was about by googling it.

The way the plugin is really intended to be used is to upload files from your computer, crop them, and then save them somewhere. Any programmatic setting of the image src will result in an AJAX request to that server, and the same CORS rules apply there.

Please read about and understand CORS before asking any follow-up questions about this. I'm happy to help out, but there is plenty of material out there that will answer your (still not plugin-related) questions.

scottcheng commented 8 years ago

@aas395 thanks a lot for the research and sorry for the delay in responding to this. I just adapted your AJAX solution in f6eb24f9030f93c5e73413bd87f7b876d4c66b9e. Would you mind taking a look?

mohsincynexis commented 8 years ago

Sure Scott, I will definitely look into it & let you know. Thanks a lot for taking this into consideration.

mohsincynexis commented 8 years ago

Scott, I used your updated jquery.cropit.js but still I am getting the same error. What am doing is when the page loaded I call a ajax request to get the modal content and append it to the body and I have a button on which I opens up the modal, so as soon as the modal opens up I set the default image as $('.image-editor').cropit('imageSrc', 'http://s1.picswalls.com/wallpapers/2014/12/09/butterfly-wallpaper_093549561_256.jpeg'); Can you let me know whats wrong in that.

Thanks Mohsin

scottcheng commented 8 years ago

@mohsincynexis As @aas395 pointed out, this is because the server hosting the image (picswalls.com) isn't allowing you to access that image via AJAX, and has nothing to do with the plugin. If you have control over that server, you need to add a Access-Control-Allow-Origin header in the response.

pierregangloff commented 8 years ago

Hi! Just throwing ideas; if you want to preload the "image-preview" with an image (ex: page loads with the user's current profile picture), you could do it simply in CSS by adding (to the ".cropit-preview"):

background-image: url("http://s1.picswalls.com/wallpapers/2014/12/09/butterfly-wallpaper_093549561_256.jpeg");

In my particular case (using Java/JSP), the server renders the HTML with the CSS dynamically based on current user on page (so, the CSS is specific to the current user on page, each user gets a different background-image URL that gets preloaded when the page renders). Of course, this would work if you're using PHP or any other server side rendering technology (just determine user on page and render proper CSS URL).

so, sample CSS:

    .cropit-preview {
        background-color: #f8f8f8;
        background-size: cover;
        border: 1px solid #ccc;
        border-radius: 3px;
        margin-top: 7px;
        width: 280px;
        height: 296px;
        background-image: url("http://s1.picswalls.com/wallpapers/2014/12/09/butterfly-wallpaper_093549561_256.jpeg");
    }

This might or might not work for you but wanted to throw it into the mix. Just in case CORS was not a "low hanging fruit" (just takes too long for someone w proper privs to reconfigure the web server where your images are stored).

Pierre