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.
Related
I know this question is asked hundreds of time in this forum, But I'm trying to download a .js file from an url in my Vue 2 application, but it's not working. Here is what I'm trying:
downloadScript() {
ApiService.post(`url`).then((res) => { // axios
try {
// Create a new link
const url = window.URL.createObjectURL(new Blob([res.data.path]));
const anchor = document.createElement("a");
anchor.href = url;.js";
anchor.setAttribute("download", "script.js");
document.body.appendChild(anchor);
anchor.click();
} catch {
//
}
});
},
This downloads a file which consists nothing but the url I've provided to the axios post request.
I'm getting API response like following:
{
"success": true,
"path": "https://something.com/files/iq-return.min.js"
}
I've to donwload the script in a file from the path
new Blob([res.data.path]) creates a Blob (which is sort-of-like-a-file) containing the text in the string you pass it.
Since that text is a URL, the file you download is a text file containing that URL.
If you want to create a Blob containing the JavaScript source code, then you need to get the JS source code. Make an HTTP request to the URL (e.g. with fetch) and put the response body in the Blob.
(Aside: don't append .js to the generated URL with you set href, that modifies the contents of the file!)
This will, of course, require permission from CORS if this is a cross-origin request.
If it isn't a cross-origin request then you can just set the href attribute to res.data.path without leaping through all these hoops.
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.
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
In my application written in Ionic 5 and Angular 8, I am in need to download multiple PDF links (file) in the same page on click of the link. I tried but the PDF links are opening in browser. Request to help me achieve this functionality.
https://stackblitz.com/edit/ionic-yzzwov?file=pages%2Fhome%2Fhome.html
The download attribute appears normal when same-origin, otherwise it will appear the same as without download attribute.
list three solutions:
Package the file into a file type that cannot be opened directly by the browser, such as ZIP
Through forwarding by the back end, the back end requests a third-party resource and returns it to the front end, which saves the file using tools such as file-Saver
If the third-party resource the URL points to has CORS configured, you can request the file through XHR
downloadFileXHR(filePath, fileName) {
var xhh = new XMLHttpRequest()
xhh.open('get', filePath)
xhh.responseType = 'blob'
xhh.onreadystatechange = function () {
if (xhh.readyState === 4 && xhh.status === 200) {
var blob = new Blob([xhh.response])
var csvUrl = URL.createObjectURL(blob)
var link = document.createElement('a')
link.href = csvUrl
link.download = fileName
link.click()
}
}
xhh.send()
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.