react-native-canvas icon indicating copy to clipboard operation
react-native-canvas copied to clipboard

How to get image drawn in canvas (The operation is insecure)

Open wojtekhorembala opened this issue 6 years ago • 23 comments

Hi i want little bit optimization my images and i use for this canvas,

Currently i have this code

const context = canvas.getContext('2d', { alpha: false });
 const image = new CanvasImage(canvas)

image.src = 'https://res.cloudinary.com/pozna/image/upload/v1564828195/fpztmajxhmphfpaechfa.jpg';
canvas.width = width;
canvas.height = 600;

image.addEventListener('load', () => {
context.drawImage(image, 0, 0, 500, 500).then(() => {
// here i want get image url or other data
 });
});

Can anyone help me? :)

wojtekhorembala avatar Aug 07 '19 12:08 wojtekhorembala

When i make this gets error from screen

image.addEventListener('load', async () => {
context.drawImage(image, 0, 0, 500, 500);
const imageData = await canvas.toDataURL();
});

image

wojtekhorembala avatar Aug 07 '19 15:08 wojtekhorembala

I am facing the same issue when I am upgrading react native to 0.59.8. and using react-native-webview 6.1.0

aravikanti avatar Aug 10 '19 01:08 aravikanti

Same issue here on react-native 0.59.10 and react-native-webview 5.8.2

PierreCapo avatar Aug 19 '19 09:08 PierreCapo

This is a general HTML Canvas issue. Check this discussion: https://stackoverflow.com/questions/25753754/canvas-todataurl-security-error-the-operation-is-insecure. Closing as it is not relevant only for this project.

iddan avatar Sep 13 '19 11:09 iddan

@PierreCapo Did you find a solution for this?

seblambla avatar Oct 17 '19 13:10 seblambla

Not at all, we reprioritized our business requirements 😬

PierreCapo avatar Oct 17 '19 13:10 PierreCapo

OK thanks anyway. (I didn't notice you are working for BAM, we've been working with them a few years ago 👍)

However I think this issue should be re-opened as I'm facing this error with a local file.

seblambla avatar Oct 17 '19 13:10 seblambla

How to fixed ?

asasugar avatar Nov 19 '19 02:11 asasugar

Any updates?

MoeMamdouh avatar Dec 07 '19 13:12 MoeMamdouh

I found the solution. Add this.crossOrigin = 'anonymous' here: https://github.com/iddan/react-native-canvas/blob/master/src/Image.js#L23 and make sure the server from you're requesting images will have CORS enabled. Unfortunately setting crossOrigin on initialized Image constructor doesn't work, so you need to modify the library.

eashish93 avatar Jan 16 '20 16:01 eashish93

Can confirm solution above works fine.

plus- avatar Feb 03 '20 10:02 plus-

If anyone wants a working module with a fix for a current issue - feel free to use react-native-canvas-anonymous.

It it is a fork with just one change (this.crossOrigin = 'anonymous') from the suggestion above.

https://www.npmjs.com/package/react-native-canvas-anonymous

dudyn5ky1 avatar Apr 05 '20 10:04 dudyn5ky1

PRs are welcome to fix it here as well.

iddan avatar Apr 05 '20 12:04 iddan

@iddan To be honest, I've discovered another problem with this.crossOrigin = 'anonymous'. It prevents image from being loaded, I'm not sure yet why.

dudyn5ky1 avatar Apr 08 '20 08:04 dudyn5ky1

I guess because it is insecure to load images from an anonymous origin

iddan avatar Apr 08 '20 09:04 iddan

I've noticed it depends on the origin, it works on my S3 urls but not cloudfront for example.

plus- avatar Apr 08 '20 09:04 plus-

Maybe it has to do with CORS headers

iddan avatar Apr 08 '20 10:04 iddan

I still have another problem, when I use canvas.fillRect and change it to base64 with toDataURL, the question arises anyway

W-alex avatar Apr 22 '20 03:04 W-alex

Anyone found a concrete solution for this? Images from links like wikimedia are working with img.crossOrigin = "anonymous" but for firebase images which I have to work with, the images don't load at all

t3chcrazy avatar Jan 15 '21 12:01 t3chcrazy

img.crossOrigin = "anonymous" may cause the image not to be displayed, so my solution is to convert the image to base64 first before adding it.

This does solve the problem but may reduce performance.

jsun969 avatar Oct 05 '21 08:10 jsun969

@eashish93 is this still working for you? I added this line to the library but i am getting the error TypeError: undefined is not a function (near '...this.postMessage...') and i can't find or figure out an other way to solve this problem.

telpat0298 avatar Jun 08 '22 20:06 telpat0298

For me, the issue was happening with a local file, which doesn't make sense as in my opinion, this security feature should only apply to cross-origin content. But I managed to find a workaround.

Before the workaround, I was trying to load the image directly passing the image URI to the image.src prop. The code was like that:

...
const { imageUri } = route.params // This is the image I'm loading. It's a local image, and it's in this format 'file://...'

function handleCanvas(canvas: Canvas | null) {
  if (!canvas) {
    return
  }

  canvas.width = Dimensions.get('screen').width
  canvas.height = Dimensions.get('screen').height

  const image = new CanvasImage(canvas)
  const context = canvas.getContext('2d')

  image.src = imageUri

  image.addEventListener('load', async () => {
    context.drawImage(image, 0, 0, canvas.width, canvas.height)
    const pixel = await context.getImageData(0, 0, 1, 1) // This line would throw the error
  })
}
...

To make it work I used expo-file-system to read the image and transform it to a base64 string, and then passed the base64 string to the image.src prop, like that:

...
const { imageUri } = route.params

async function handleCanvas(canvas: Canvas | null) {
  if (!canvas) {
    return
  }

  canvas.width = Dimensions.get('screen').width
  canvas.height = Dimensions.get('screen').height

  const image = new CanvasImage(canvas)
  const context = canvas.getContext('2d')

  const base64Image = await FileSystem.readAsStringAsync(imageUri, {
    encoding: FileSystem.EncodingType.Base64,
  })
  image.src = `data:image/png;base64,${base64Image}`

  image.addEventListener('load', async () => {
    context.drawImage(image, 0, 0, canvas.width, canvas.height)
    const pixel = await context.getImageData(0, 0, 1, 1) // No more errors here
  })
}
...

Of course, it has the performance downside, but it suits my needs. Also, my problem was with local images, but I think this solution could work for remote images as well (I'm not sure, I haven't tested). I think that a possible approach would be to download the remote image, and then make the same process as I did.

Hope this helps someone!

Harukisatoh avatar Oct 04 '22 00:10 Harukisatoh

I didn't have any luck with the crossOrigin setting, so I ended up using RNFS.downloadFile and then RNFS.readFile and made a base64 PNG data URL of the data.

esbenvb avatar Jul 31 '23 20:07 esbenvb