preface
In front site download file, this is a very common demand, long ago already has a variety of solutions, why so old articles, only recently in with a new person, he seems to be a lot of have a little knowledge, also encountered problems we must pass through “cannot download TXT, PNG file” typical problem, I give him to summarize several ways to download. By the way, maybe someone really needs it.
Form submission
This was the traditional approach used in the old days, when there weren’t so many new features to use.
Add a click event for a download button, dynamically generate a form when clicked, and use the form submission function to download the file (in fact, the form submission is to send a request).
Here’s how to generate a form and what it looks like:
/** * download file * @param {String} path - requested address * @param {String} fileName - fileName */
function downloadFile (downloadUrl, fileName) {
// Create form
const formObj = document.createElement('form');
formObj.action = downloadUrl;
formObj.method = 'get';
formObj.style.display = 'none';
// create input
const formItem = document.createElement('input');
formItem.value = fileName; // Pass the value of the parameter
formItem.name = 'fileName'; // The field name of the parameter to be passed
// Insert it into the page
formObj.appendChild(formItem);
document.body.appendChild(formObj);
formObj.submit(); // Send the request
document.body.removeChild(formObj); // Clear it after sending
}
Copy the code
advantages
- Traditional method, good compatibility, will not appear URL length limit problem
disadvantages
- There is no way to know the download progress
- Unable to download files that can be previewed directly by the browser (TXT/PNG, etc.)
The open, or the location. The href
The simplest and most direct way is actually the same as a tag to access the download link
window.open('downloadFile.zip');
location.href = 'downloadFile.zip';
Copy the code
Of course, the address can also be the address of the interface API, not just a link address.
advantages
- Simple, convenient and direct
disadvantages
- URL length limits occur
- Pay attention to URL encoding
- The types of files that can be directly viewed by the browser are not available for download, such as TXT, PNG, JPG, and GIF
- If you cannot add headers, authentication cannot be performed
- There is no way to know the download progress
A Download of the tag
As we know, the A tag can access the address of the downloaded file, and the browser helps with the download. But for the browser support direct browsing TXT, PNG, JPG, GIF and other files, it does not provide direct download (can right click from the menu to save as).
To solve this problem of browsing without downloading, you can use the Download attribute.
Download attribute is a new attribute in HTML5, can I use Download
The overall compatibility is very good, basically can be divided into IE and other browsing. But some information to note:
- Edge 13 crashes when trying to download a data URL link.
- Chrome 65 and later only supports same-origin download links.
- Firefox only supports same-origin download links.
Based on the above description, if you try to download a cross-domain link, the effect of Download will actually be lost, as if you did not set Download. What the browser previews is still previewed, not downloaded.
Simple usage:
<a href="example.jpg"</a>Copy the code
You can add property values to specify the file name of the download, that is, rename the downloaded file. If this parameter is not specified, the original file name is used by default.
<a href="example.jpg" download="test"> Click download </a>Copy the code
As above, an image named Test has been downloaded
Monitor whether Download is supported
To find out whether the browser supports the Download attribute, a simple line of code can tell the difference
const isSupport = 'download' in document.createElement('a');
Copy the code
For files that can not be downloaded and browseable under the cross-domain, in fact, you can negotiate with the back end, do one more layer of forwarding at the back end, and finally return the file link to the front end with the same domain as the download page.
advantages
- Can solve the problem of directly downloading browserable files
disadvantages
- You need to know where to download the file
- Cannot download files that can be browsed by browsers across domains
- Compatibility issues, especially IE
- Authentication cannot be performed
Use Blob objects
The advantage of this method over the previous method of using a tag Download directly is that in addition to using the known file address path for download, it can also send ajax request API to get the file stream for download. After all, sometimes the backend will not provide you with a download address to access directly, but will call the API.
Blob objects are used to stream files into Blob binary objects. This object is compatible, but it needs to be noted
- Internet Explorer 10 or later is not supported.
- Access it in Safari
Blob Url
orObject URL
The current is defective, as passed belowURL.createObjectURL
Generated links.caniuse
The website points out that
Safari has a serious issue with blobs that are of the type application/octet-stream
CreateObjectUrl to generate the URL address, assign the value to the href attribute of a tag, and download the binary data.
/** * download file * @param {String} path - Download address/download request address. * @param {String} name - The name/rename of the downloaded file (preferably with a suffix for compatibility reasons) */
downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
// If Internet Explorer 10 or later does not support the Download attribute, use the msSaveOrOpenBlob method. However, msSaveOrOpenBlob is also not supported in Internet Explorer 10 or later
if ('msSaveOrOpenBlob' in navigator) {
navigator.msSaveOrOpenBlob(this.response, name);
return;
}
// const blob = new Blob([this.response], { type: xhr.getResponseHeader('Content-Type') });
// const url = URL.createObjectURL(blob);
const url = URL.createObjectURL(this.response);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a); URL.revokeObjectURL(url); }}; }Copy the code
This method cannot be without setting the download attribute of the A tag. Since the return data type is set to Blob (xhr.responseType = ‘Blob’) when the request is sent, target.response is a Blob object with two properties size and type printed out. Although the type attribute already specifies the type of the file, it is safe to specify the suffix in the Download attribute value. If Firefox does not specify the type of the downloaded file, it will not recognize the type.
You may notice that there are two comments in the code above, but there is another way to write it. If the request is sent without setting xhr.responseType = ‘blob’, the default Ajax request returns DOMString data, which is a string. This is where the code for the two comments is needed, converting the returned text into a Blob object and creating a Blob URL, which comments out the original const URL = url.createObjecturl (target.response).
advantages
- Can solve the problem of directly downloading browserable files
- You can set header to add authentication information
disadvantages
- Internet Explorer 10 is not available due to compatibility problems. Safari can keep an eye on usage
Using base64
The only difference is that the Blob object is used to generate Blob urls, while the Data URL is generated in base64 encoded URL form.
/** * download file * @param {String} path - Download address/download request address. * @param {String} name - The name of the downloaded file (preferably with a suffix for compatibility reasons) */
downloadFile (path, name) {
const xhr = new XMLHttpRequest();
xhr.open('get', path);
xhr.responseType = 'blob';
xhr.send();
xhr.onload = function () {
if (this.status === 200 || this.status === 304) {
const fileReader = new FileReader();
fileReader.readAsDataURL(this.response);
fileReader.onload = function () {
const a = document.createElement('a');
a.style.display = 'none';
a.href = this.result;
a.download = name;
document.body.appendChild(a);
a.click();
document.body.removeChild(a); }; }}; }Copy the code
advantages
- Can solve the problem of directly downloading browserable files
- You can set header to add authentication information
disadvantages
- Internet Explorer 10 is unavailable due to compatibility issues
About File name
Sometimes we don’t know the file name before we send the download request, or the file name is provided by the back end and we have to find a way to get it.
Content-Disposition
When we return the file stream, we look at the message returned by the interface in the browser and we see a header: Content-disposition
Content-Disposition: attachment; filename=CMCoWork__________20200323151823_190342.xlsx; filename*=UTF-8' 'CMCoWork_%E4
Copy the code
The values above are examples.
It contains the file name, and we can try to get the file name. We can see that filename= and filename*=, the latter may not have the form, which is not supported in older browsers or in some browsers, filename* uses the encoding specified in RFC 5987.
So if you want to get the file name, you’re just going to intercept the values of these two fields in this string.
If you look at the example above, you might see how it’s weird. Yes, if the name is In English, that’s fine, but if it has Chinese or other special symbols, it needs to be handled well
filename
, requires the backend to handle the encoding form, but even if it does, it will parse differently for each browser. It’s a tough guy to handle. That’s why there’s a backfilename*
filename*
, is a modern browser support, to solvefilename
Is generallyUTF-8
And we usedecodeURIComponent
We can decode it, restore it to its original form. Of course, you have to put the values in before decodingUTF-8''
Let’s get rid of that.
So, before we do that, we need to understand that content-disposition is not 100 percent what you would expect unless your filename is all English numbers.
We extract the filename value:
XHR is an XMLHttpRequest object
const content = xhr.getResponseHeader('content-disposition'); // Note that the header is all lowercase, as is the custom header
if (content) {
let name1 = content.match(/filename=(.*); /) [1]; // Get the value of filename
let name2 = content.match(/filename\*=(.*)/) [1]; // Get the value of filename*
name1 = decodeURIComponent(name1);
name2 = decodeURIComponent(name2.substring(6)); // utf-8"
}
Copy the code
If name1 contains Chinese characters or special characters, there is a risk that it will not be able to restore the real filename.
defects
- The file name is not in full numeric English, if the browser only supports it
filename
The encoding of the file name obtained may be problematic.
The custom header
It’s essentially the same as content-disposition, except instead of using the default header, we’re going to create our own response header, which the back end decides to encode, and the front end takes the custom header and uses the corresponding decoding. Use decodeURIComponent.
But it’s important to remember that in cross-domain situations, the header gets only the default six base fields: Cache-Control, Content-Language, Content-Type, Expires, Last-Modified, and Pragma.
So if you want to get another header, you have to go back and set it
Access-Control-Expose-Headers: Content-Disposition, custom-header
Copy the code
In this way, the front end gets the corresponding exposed header field, and it should be noted that Content-Disposition also needs to be exposed.
rename
Here is an additional method, which is used when you know the full name of a file and want to rename it, but the file has the same suffix.
function findType (name) {
const index = name.lastIndexOf('. ');
return name.substring(index + 1);
}
Copy the code
Please do not reprint without permission
My wechat official account