JavaScript can't download images programmatically (getting 403) - javascript

In my website I've a button that whenever user clicks on it downloads him a random image, here is the code to download an image:
const downloadImg = (src) => {
const imgName = src.replace(/^.*[\\\/]/, '');
var a = document.createElement('a');
a.href = src;
a.download = imgName;
a.click();
};
This works completely fine from images that are from open websites, like google.com or Wikipedia commons
However, for images from websites like Pixabay, Pexels, Freepik instead of downloading the Image it opens the image URL in the same tab and gives me 403 forbidden error in the console
I completely understand why this error happens, but what I don't understand is how to fix it? If I right-clicked on the image then hit save image as no error will appear and I will be able to download the image normally, how can I do this with javascript programmatically?

It works using the approach from accepted answer here: Chrome 65 blocks cross-origin <a download>. Client-side workaround to force download? , with a minor change. Instead of using mode "cors" use "no-cors". Apparently there is a cors error with some domains when downloading directly from url.
Updated: It does not work if the server does not allow cros-origin requests. Making the request with "no-cors" will succeed, but the response body will not be available. So this is NOT a solution.

You can use javascript fetch to download the random selected image from url address.
Code:
JS:
<script>
function downloadImage(url, name){
fetch(url)
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// the filename you want
a.download = name;
document.body.appendChild(a);
a.click();
//window.URL.revokeObjectURL(url);
})
.catch(() => alert('An error sorry'));}</script>
HTML:<button onclick="downloadimage('https://pixabay.com/get/g2cc1f3e1fe58926edc20db6cf67be6dd1614d93b06e934118288e4c57d5228c60c50de32506ac83ffdabc6fe20a6a01b3c7504b82965e6043e9038185180f3ae_1920.jpg', 'download.jpg')" >download</button>
example:
https://viena.lovestoblog.com/bakDownload/bak.php

Related

Download image from a third-party server button

In any browser, if you saw an image, you can right-click on it and click "save as" to download it.
I'm trying to make a button to download an image
The download button should download the image above, the barcode.
I am using react, not sure if this has something to do with the answers.
I read that you can use the <a/> tag with the download attribute, however, I'm on Firefox, and it's redirecting me to a page where the barcode image is hosted, and it's not opening the download window instead:
The code is pretty simple, it look as follows:
<a href='https://barcode.tec-it.com/barcode.ashx?data=${product.barcode}&code=&multiplebarcodes=true&backcolor=%23ffffff&quietzone=10&quietunit=Px&size=Small' download>click me</a>
From the MDN docs:
download only works for same-origin URLs, or the blob: and data:
schemes.
I want to implement this, how can I do it?
I'm not the owner of the server where the image is hosted.
Can we do that in 2023?
The other questions are mixing between local images and images hosted on other servers.
So I thought I could create this thread for people interested only in images on third party servers. - so we are all front-end here, no back-end related stuff.
I think your question refers to this old question.
You need something on the server to send a Content-Disposition header to set the file as an attachment so the browser will not try to handle the file internally.
Please see:
href image link download on click
It only works on the same website, not an external link. Try an image of the same website. Ex: <a href="images/detailed-product.png" download>click</a>
You'll need to proxy the request to avoid CORS issues. As the article states it's better to deploy your own proxy, but you can test with a free one, eg: https://codetabs.com/cors-proxy/cors-proxy.html
StackBlitz updated example:
const downloadButton = document.querySelector('button');
downloadButton.onclick = () => {
console.log('download button clicked');
downloadImage(
'https://api.codetabs.com/v1/proxy?quest=https://barcode.tec-it.com/barcode.ashx?data=${product.barcode}&code=&multiplebarcodes=true&backcolor=%23ffffff&quietzone=10&quietunit=Px&size=Small'
);
};
async function downloadImage(imageSrc) {
try {
const image = await fetch(imageSrc);
const imageBlob = await image.blob();
const imageURL = URL.createObjectURL(imageBlob);
const link = document.createElement('a');
link.href = imageURL;
link.download = 'image.jpg';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
} catch (error) {
console.log(error);
}
}
<button>Download image</button>
To do that, you can utilize the createObjectURL static method from URL to create download link for the image. Then, we create temporary <a> in a variable to open that link programmatically.
async function downloadImage(imageSrc) {
const image = await fetch(imageSrc)
const imageBlob = await image.blob()
const imageURL = URL.createObjectURL(imageBlob)
const link = document.createElement('a')
link.href = imageURL
link.download = 'myimage.jpg'
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}

How to change the extension name of a file before downloading?

I have a video file hosted which looks like this:
https://cdn.shopify.com/s/files/1/0664/3037/0044/files/video
Right now, the video file has no extension at all. Using Javascript, how can I download the file with .mp4 extension. I need a way to change its filename to [filename].mp4 and then download.
I tried the following but it downloads the file without the extension.
function downloadURI(uri, name) {
var link = document.createElement("a");
link.download = name;
link.href = uri;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
}
downloadURI("https://cdn.shopify.com/s/files/1/0664/3037/0044/files/video", "video.mp4");
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-download
download only works for same-origin URLs, or the blob: and data:
schemes. If the Content-Disposition header has different information
from the download attribute, resulting behavior may differ:
If the header specifies a filename, it takes priority over a filename
specified in the download attribute. If the header specifies a
disposition of inline, Chrome and Firefox prioritize the attribute
and treat it as a download. Old Firefox versions (before 82)
prioritize the header and will display the content inline.
I tried your code on a different origin and it wouldn't download but simply redirect to the URL, also it doesn't send Content-Disposition, so it looks like since you're testing this on a different origin, it just redirects to the file link, discarding download because it's on a different origin, and your browser is configured to download it. My browser is configured to autoplay videos.
However, I see the URL you provided has CORS *, meaning any origin can access it. So, an alternative you can do is, download the file, and create a blob, then send download to browser.
function downloadURI(uri, name) {
fetch(uri).then((response) => response.blob())
.then((blobresp) => {
var blob = new Blob([blobresp], {type: "octet/stream"});
var url = window.URL.createObjectURL(blob);
var link = document.createElement("a");
link.download = name;
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
delete link;
})
}
downloadURI("https://cdn.shopify.com/s/files/1/0664/3037/0044/files/video", "video.mp4");
const myRenamedFile = new File([myFile], 'my-file-final-1-really.txt');
Taken from this website which may be a better at explaining than me:
https://pqina.nl/blog/rename-a-file-with-javascript/
alternatively, there seems to be lots of APIs or Node JS that can manage this. This will allow you to rename the file after it's been downloaded and is now a local file.

Cross-origin download of images in js

I am using Chrome 79.
I am trying to start download of an image within a html page. The image is located on the same server as the webpage, but it is served via nginx server via different port. This apparently gets me to cross-origin security problems.
First I tried to use the standard
var a = document.createElement('a');
a.download = "file.bmp";
a.href = "http://xxxxxxx/xxx/somefile.bmp";
document.body.appendChild(a);
a.click();
a.remove();
Interestingly this gets me to the behaviour that my image is displayed in a new tab, but not downloaded. I assume this is due to cors reasons?
After reading discussion on this topic in :
Chrome 65 blocks cross-origin <a download>. Client-side workaround to force download?
I tried to use the approach presented there using the fetch function. Unfortunatelly this does not solve my problem. I get a response with "opaque" type and my blob is undefined...
var url = "http://xxxxxxxxx/xxx/somefile.bmp";
fetch(url, {
mode: 'no-cors'})
.then(function(response){handleBlob(response);})
.catch(e => console.error(e));
-------
function handleBlob(response)
{
var blob = response.blob;
var blobUrl = URL.createObjectURL(blob);
var ImageTest = document.getElementById("DummyLink");
ImageTest.download = "file.bmp";
ImageTest.href = blobUrl;
ImageTest.click();
}
I thought this is all very strange - the server name of the resource is same as of the webpage, so I would assume there is no real security danger here...
Any suggestions or workarounds on how to trigger a download of an image to the disk would be greatly appreciated.
Not 100% sure what your trying to do.
var url = 'http://webpage.com/images/images.jpg',
var img = document.createElement('img');
img.src = src;
document.getElementById("DummyLink").appendChild(img);

HTML anchor tag download attribute not working in Firefox for jpg and png files

In my web application I have supported user to upload any type of document (.png, .jpg, .docx, .xls, ... ) I'm trying to implement download functionality for these documents.
In Google Chrome if you click on Download link Save dialog is shown for all above documents.
In Mozilla Firefox for docx and xls works fine, Save dialog is shown but for .png and .jpg download tag is not working as expected i.e., download dialog or Save dialog does not appear, it directly open that image.
My code:
Download
I have tried almost all solutions mentioned on stackoverflow and suggested by Google. But most of them says that 'check firefox version' and other changes like:
try adding the element to the DOM before triggering the click
Remove filename from download tag it is of boolean type and etc.
I have also tried w3schools lesson on anchor tag and download attribute but nothing seems to be working.
My Mozilla Firefox version is: 38.0.5
P.S.: in chrome as well as in firefox .docs, .xls, .pdf documents work fine, problem is for .png and .jpg in firefox.
Firefox will handle png and jpeg using default handling, which is to inline them in the document. When clicking a link, even if download attribute is defined, seem to make Firefox think it has a new image ignoring the download aspect of it. This may be a temporary bug.
Here is a way, admittedly not super-elegant, to get around this problem forcing the image to be interpreted as an octet-stream.
It does not work inline on Stackoverflow so you have to test it on jsFiddle.
The code does the following:
Scans the document for a-tags.
Those which has data-link set will have a common click-handler attached.
When clicked the link is extracted from the data-link attribute (href is se to #), loaded as an ArrayBuffer via XHR (CORS requirements applies, not a problem in this case), and is converted to an Object-URL with the Blob set to mime-type octet/stream
The Object URL is set as window.location to redirect to this binary data which will make the browser ask user to download the file instead.
var links = document.querySelectorAll("a"), i = 0, lnk;
while(lnk = links[i++]) {
if (lnk.dataset.link.length) lnk.onclick = toBlob;
}
function toBlob(e) {
e.preventDefault();
var lnk = this, xhr = new XMLHttpRequest();
xhr.open("GET", lnk.dataset.link);
xhr.responseType = "blob";
xhr.overrideMimeType("octet/stream");
xhr.onload = function() {
if (xhr.status === 200) {
window.location = (URL || webkitURL).createObjectURL(xhr.response);
}
};
xhr.send();
}
Example tag:
Click to download
The drawback is that you'll loose the extension in the filename.
This is also possible to do using a Data-URL, but a data-url has a 166% overhead compared to using ArrayBuffer and a blob.
I had a similar problem with firefox not handling the download attribute, even for same-domain files.
My target files are actually hosted on AWS, so they are cross-domain. I got around this with a same-domain endpoint that downloads the remote file and pipes it to the client.
const express = require('express')
const {createWriteStream} = require('fs')
const downloadVideo = (url) => { return new Promise((resolve, reject) => {
const filePath = `/tmp/neat.mp4`
const ws = createWriteStream(filePath)
request(url, {}, (error, response, body) => {
if(error) { return reject(error) }
resolve(filePath)
}).pipe(ws)
})}
app.get('/api/download', async (req, res) => {
const videoPath = await downloadVideo(req.query.url)
res.sendFile(videoPath)
})
On the client, I send the file path to the download endpoint to get a blob back, which is then converted to an object url. From there, it's standard download attribute stuff.
async download(remoteFilePath){
const a = document.createElement('a')
const dlURL = `/api/download?url=${encodeURIComponent(remoteFilePath)}`
const blob = await fetch(dlURL).then(res => res.blob())
a.href = URL.createObjectURL(blob)
a.setAttribute('download', 'cool.mp4')
document.body.appendChild(a)
a.click()
a.remove()
}
As you are using HTML5 attribute, each browser handling differently. So use https://github.com/dcneiner/Downloadify for client side forceful download instead of viewing in browser.

how to set content-disposition = attachment via javascript?

How can I set content-disposition = attachment via javascript?
Basically, I would like to force a "SaveAs" operation after a page has loaded via Javascript, using Firefox.
How can I do this ?
Content-Disposition is a response header, ie. the server must return it. You can't achieve this with client-side javascript.
HTML only: use the download attribute.
<a download href="http://upload.wikimedia.org/wikipedia/commons/b/bb/Wikipedia_wordmark.svg">Download</a>
Javascript only: you can save any file with this code:
function saveAs(uri) {
var link = document.createElement('a');
if (typeof link.download === 'string') {
link.href = uri;
link.setAttribute('download', true);
//Firefox requires the link to be in the body
document.body.appendChild(link);
//simulate click
link.click();
//remove the link when done
document.body.removeChild(link);
} else {
window.open(uri);
}
}
var file = 'http://upload.wikimedia.org/wikipedia/commons/b/bb/Wikipedia_wordmark.svg';
saveAs(file);
1.Use Anchor "download"(HTML5) attribute
<a href='abc.pdf' download>Click Here</a>
2.Create href programmatically using js,
const link = document.createElement('a');
link.href = '/xyz/abc.pdf';
link.download = "file.pdf";
link.dispatchEvent(new MouseEvent('click'));
According to Mozilla doc Anchor element, download attribute(HTML5) instructs browsers to download a URL instead of navigating to it.
Important Notes:
This attribute only works for same-origin URLs.
Although HTTP(s) URLs need to be in the same-origin, blob: URLs and data: URLs are allowed so that content generated by JavaScript, such as pictures created in an image-editor Web app, can be downloaded.
So the above js method to create anchor element dynamically and using it download the file will only work for the same origin files i.e
There are two ways to handle this problem ->
Client-side
Server-side
Client-side solution:->
A work around for this problem, refrenced in second Note i.e a blob object can be used, with the help of fetch js API
url = 'https://aws.something/abc.pdf';
fetch(url, {
method: 'GET',
}).then(function(resp) {
return resp.blob();
}).then(function(blob) {
const newBlob = new Blob([blob], { type: "application/pdf", charset: "UTF-8" })
// IE doesn't allow using a blob object directly as link href
// instead it is necessary to use msSaveOrOpenBlob
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(newBlob);
return;
}
const data = window.URL.createObjectURL(newBlob);
const link = document.createElement('a');
link.dataType = "json";
link.href = data;
link.download = "file.pdf";
link.dispatchEvent(new MouseEvent('click'));
setTimeout(function () {
// For Firefox it is necessary to delay revoking the ObjectURL
window.URL.revokeObjectURL(data), 60
});
});
Server-side solution:->
The other option is if you can control the server side response headers then this may be the best option.
In a regular HTTP response, the Content-Disposition response header is a header indicating if the content is expected to be displayed inline in the browser, that is, as a Web page or as part of a Web page, or as an attachment, that is downloaded and saved locally.
e.g
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"
For a file hosted on AWS , its response headers can be edited, these can be changed in meta data, add the content disposition header in the meta data in the file or the folder propertities, add key as content-disposition and value as attachment,
content-disposition : attachment
Now whenever this file is hit from a browser it would always download instead of opening, now if u use this file link in a anchor tag it would be downloaded directly with use of download html tag.

Categories