onClick "save image as" in React - reactjs

I want to simulate Save Image As when the user clicks on an image.
I read this and this thread but I couldn't find the answer.
I'm using CRA and the image is not on my server. Is there a way where I can achieve this?
Here is a Sandbox:
https://codesandbox.io/s/save-image-as-react-pm3ts?file=/src/App.js
<a
href="https://img.mipon.org/wp-content/uploads/2019/12/14094031/logo-cover-website.png"
download
>
<img
download
alt="Mipon"
style={{ width: "300px" }}
onClick={() => console.log("should trigger save image as")}
src="https://img.mipon.org/wp-content/uploads/2019/12/14094031/logo-cover-website.png"
/>
</a>

The download attribute will not work as intended since Blink engine will block cross-origin <a download>. Checkout Deprecations and removals in Chrome 65.
To avoid what is essentially a user-mediated cross-origin information
leakage, Blink will now ignore the presence of the download attribute
on anchor elements with cross origin attributes. Note that this
applies to HTMLAnchorElement.download as well as to the element
itself.
You may try to draw the image to a canvas element and download its dataURL. But this approach will work only if the requested domain has an Access-Control-Allow-Origin headers that allow shared requests. Images without CORS approval will taint the canvas and you will encounter error Tainted canvases may not be exported download image when you try to use methods like toDataURL(). In that case, you can host the image with services like Cloudinary and the download with canvas approach will work.
Here is a function to download an image using canvas, you may use it in a onclick event handler:
function downloadImage(src) {
const img = new Image();
img.crossOrigin = 'anonymous'; // This tells the browser to request cross-origin access when trying to download the image data.
// ref: https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image#Implementing_the_save_feature
img.src = src;
img.onload = () => {
// create Canvas
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// create a tag
const a = document.createElement('a');
a.download = 'download.png';
a.href = canvas.toDataURL('image/png');
a.click();
};
}

Related

html2canvas not including fetched image in downloaded png

I'm trying to use the movie db API to fetch a movie poster and allow the user to put text next to it, then allow them to download that component using html2canvas. The downloaded image works, it has the right colors and text just not the movie. The image fills it's container using a background-image style but I also tried using an img tag but that didn't work either. Here is the download function:
const handleDownload = async () => {
const element = ref.current;
const canvas = await html2canvas(element, {useCORS: true, allowTaint: true});
const data = canvas.toDataURL('image/png');
const link = document.createElement('a');
if (typeof link.download === 'string') {
link.href = data;
link.download = 'image.png';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} else {
window.open(data);
}
}
this is the error that I'm getting:
Here is what it's supposed to capture:
Here is what it actually captures:
I tried using background-image style for the container and I also tried using an img tag and it was the same error. I also tried setting useCORS to false which also didn't work.
This solution was thanks to #ChristianUnnerstall
The problem was that it was being blocked by cors. I had to add crossOrigin="anonymous" to the img tag that was rendered from the imdb database:
<img
id="chosen-image"
src={
selectedMovieImg
? `${selectedMovieImg}?not-from-cache-please}`
: ""
}
alt="movie poster"
crossOrigin="anonymous"
/>
I had to add ?not-from-cache-please so cors sees it as a different request and it will be rendered to the dom and selectedMovieImg is a variable that holds the imdb link to the resource!

html2canvas failing to capture Google static map

I'm creating a booking reference for the user to download after successfully making a booking. It looks like this in the browser:
However, after using html2canvas to get a screenshot of the component in React, I get this:
I suspect the issue has to do with the fact that the static map's use is protected by an API key, causing issues to render the image in an external view.
This is the callback that fires on hitting the share button, which for now just downloads the png (which is then converted to pdf using jsPDF):
const share = () => {
const input = document.getElementById("trip-summary");
if (input === null) return;
html2canvas(input!).then((canvas) => {
var image = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
const pdf = new jsPDF();
// #ts-ignore
pdf.addImage(image, "PNG", 0, 0);
pdf.save("download.pdf");
});
};
It looks like an external source of content. You should set the allowTaint option to true.
html2canvas(input, { allowTaint: true }).then(...);
Just wanted to add to the answer above.
It achieves exactly what I want, but you just need to set useCORS to true in the options as well
html2canvas(input!, { allowTaint: true, useCORS: true }).then(...)
, otherwise you'll get
Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

How to fix "Access to image [Image Url] from origin http:localhost:3000 has been blocked by CORS Policy (React & HTML Canvas)

I have a code that takes a image url and draws it on the canvas however i get faced with this error and my image does not appear on the canvas , the canvas stays empty.
ERROR
Access to image at 'https://storage.googleapis.com/object-cut-images/54062ac7-59dd-4684-baf4-840e6c5a2c3c.png' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Below IS My Code for drawing the image on the canvas
const Testing = () => {
const canvasRef = useRef(null)
useEffect(() => {
const canvas = canvasRef.current
canvas.width = 100
canvas.height = 100
const context = canvas.getContext('2d')
const image = new Image()
image.src= "https://storage.googleapis.com/object-cut-images/54062ac7-59dd-4684-baf4-840e6c5a2c3c.png"
image.crossOrigin = "Anonymous";
image.onload = () => {
context.drawImage(image , 0 , 0 , canvas.width , canvas.height)
console.log(context.getImageData(0, 0, canvas.width , canvas.height).data.length)
}
}, [])
return (
<>
<canvas className={styles.canvasCont} ref={canvasRef}>
</canvas>
</>
)
}
As can be seen in the code I have even added "image.crossOrigin" to be anonymous so why is it not functioning properly? Am I missing something?
I was able to find the solution to this by simply setting up a proxy in my package.JSON file like so:
"proxy": "http://localhost:3000"
And removed the "image.crossOrigin" and it works perfectly now!
According to the error, it said "origin 'http://localhost:3000'" and that is why you see the proxy is "http://localhost:3000".

Save PDF file loaded in iframe

I am trying to save a pdf file that is loaded in an iFrame. There is by default a button in the iFrame to save the file but I want an extra button (outside the iFrame) to save the file.
<iframe id="labelFrame" src="loadedFile.pdf"></iframe>
<button id="savePDF">Download File</button>
In javascript:
$('#savePDF').click(function(){
var save = document.getElementById('labelFrame');
//Save the file by opening the explorer for the user to select the place to save or save the file in a default location, how do I do this?
}
What is the best way to reach this?
I needed an answer to this question as well and found a solution.
When displaying a PDF in an IFrame the browser will render it in an <embed> element and from there we cant use it in javascript as far as i know.
We'll need to use XMLHttpRequest to get the PDF from a server as a Blob object only then we can both display it and save it using javascript.
var iframe = document.getElementById('labelFrame'),
saveBtn = document.getElementById('savePDF'),
pdfUrl = 'loadedFile.pdf';
var xhr = new XMLHttpRequest();
xhr.open("GET", pdfUrl);
xhr.responseType = 'blob'; // <- important (but since IE10)
xhr.onload = function() {
var blobUrl = URL.createObjectURL(xhr.response); // <- used for display + download
iframe.src = blobUrl
saveBtn.onclick = function() {
downloadBlob(blobUrl, 'myFilename.pdf');
}
};
xhr.send();
The xhr.onload function will set to src of the iframe and add the onclick handler to the save button
Here is the downloadBlob() function that i've used in the example
function downloadBlob(blobUrl, filename) {
var a = document.createElement('a');
a.href = blobUrl;
a.target = '_parent';
// Use a.download if available. This increases the likelihood that
// the file is downloaded instead of opened by another PDF plugin.
if ('download' in a) {
a.download = filename;
}
// <a> must be in the document for IE and recent Firefox versions,
// otherwise .click() is ignored.
(document.body || document.documentElement).appendChild(a);
a.click();
a.remove();
}

Protractor e2e test case for downloading image file

I have a web service that generates an dynamic image when /image.gif is called.
How can I get the protractor test to download the file and do an MD5sum on the result?
I had a look at Protractor e2e test case for downloading pdf file but it relies on the browser having a download link to click.
There is no page hosting this image.
I could not get the image to download on my windows setup, using Leo's answer no errors, but I found the following that allowed me to get the base64 data uri which I can "expect" on.
Convert an image into binary data in javascript
it('Image is version1)', function() {
browser.ignoreSynchronization = true
browser.get(targetUrl);
return browser.executeScript(function() {
var img = document.querySelector('img');
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}).then(function (data) {
expect(data).toEqual(base64VersionOfVersion1);
});
});
You should follow my Chrome configuration on that pdf-question link.
Then, instead of clicking something, browser.get('/image.gif'); should trigger the download.
Wait for the download to complete following Stephen Baillie solution or similar.
Finally, NodeJS side of things for the MD5 part, you can use execSync or something more fancy but execSync will do:
var execSync = require('exec-sync');
var md5sum = execSync('md5sum ' + pathToDownloadedFile).split(' ')[0]
// e.g. md5sum //=> "c9b833c49ffc9d48a19d0bd858667188"

Resources