iddan / react-native-canvas

A Canvas component for React Native
MIT License
992 stars 172 forks source link

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

Open wojtekhorembala opened 5 years ago

wojtekhorembala commented 5 years ago

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 commented 5 years ago

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

aravikanti commented 5 years ago

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

PierreCapo commented 5 years ago

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

iddan commented 5 years ago

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.

seblambla commented 5 years ago

@PierreCapo Did you find a solution for this?

PierreCapo commented 5 years ago

Not at all, we reprioritized our business requirements 😬

seblambla commented 5 years ago

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.

asasugar commented 4 years ago

How to fixed ?

MoeMamdouh commented 4 years ago

Any updates?

eashish93 commented 4 years ago

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.

plus- commented 4 years ago

Can confirm solution above works fine.

dudyn5ky1 commented 4 years ago

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

iddan commented 4 years ago

PRs are welcome to fix it here as well.

dudyn5ky1 commented 4 years ago

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

iddan commented 4 years ago

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

plus- commented 4 years ago

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

iddan commented 4 years ago

Maybe it has to do with CORS headers

W-alex commented 4 years ago

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

t3chcrazy commented 3 years ago

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

jsun969 commented 3 years ago

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.

telpat0298 commented 2 years ago

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

Harukisatoh commented 2 years ago

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!

esbenvb commented 1 year ago

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.