The front-end implements the file download method; and the back-end returns the blob file stream for display.

Front-end downloads are generally divided into two situations. One is that the back-end directly gives a file address and can be downloaded by opening it through a browser. The other is that a request needs to be sent, the back-end returns binary stream data, and the front-end parses the stream data and generates a URL. , to achieve downloading.

1. a tag:

Downloading files through the download attribute of the a tag is the simplest and most common method. Let’s look at the sample code first:

<a href="http://www.baidu.com" download="baidu.html">下载</a>

Through the above example, we found that clicking download jumps to Baidu's homepage without actually downloading the file.

Because the a tag download can only download files from the same source, if it is a cross-domain file , including pictures, audio and video and other media files, they are previewed and cannot be downloaded .

The above code implements file downloading directly by writing a tag. We can also implement it through js. The code is as follows:

const download = (filename, url) => {
    
    
    let a = document.createElement('a'); 
    a.style = 'display: none'; // 创建一个隐藏的a标签
    a.download = filename;
    a.href = url;
    document.body.appendChild(a);
    a.click(); // 触发a标签的click事件
    document.body.removeChild(a);
}

The effect is the same as above, it jumps to Baidu's homepage without downloading the file.

The focus here is the download attribute of the a tag, which is new to HTML5.

Its function is to specify the downloaded file name. If not specified, the downloaded file name will be determined based on the Content-Disposition of the requested content. If there is no Content-Disposition, the last part of the requested URL will be used as the file name. .

  • What is Content-Disposition?

Content-Disposition refers to a parameter in the HTTP response header used to control the client's behavior of saving files. This parameter can be set to "inline" or "attachment". The former means to open the file directly in the browser, and the latter means to let the browser download the file.

For example, Content-Disposition:attachment;filename="example.mp4" means to force the browser to download the example.mp4 file in the current HTTP response. Content-Disposition:inline;filename="example.pdf" will directly open the example.pdf file in the current HTTP response in the browser.

  • The role and usage of Content-Disposition

Content-Disposition is mainly used to tell the browser how to handle files returned by the server. When the server returns a file, the browser will decide how to process it based on the MIME type of the file by default. However, if the server wants the browser to take a specific action, such as downloading a file, it can use Content-Disposition to tell the browser what action to take.

In practical applications, Content-Disposition is usually set on the server side rather than on the client side. For example, in Spring framework, you can set Content-Disposition for the response by adding the following code.

<response-headername="Content-Disposition"expression="attachment;filename=myfile.pdf"/>

The above code indicates that the Content-Disposition of the response is "attachment" and specifies that the downloaded file name is "myfile.pdf".

2. window.open

Using the a tag can also be achieved through window.open. The effect is the same. The code is as follows:

window.open('http://www.baidu.com', '_blank', 'download=baidu.html')

_blank means opening in a new page. If not specified, it will open in the current page by default; the download attribute of the a tag can also be used. Cross-domain files cannot be downloaded either .

3. The backend provides an interface for downloading through file streams.

3.1 Solution 1 a tag + download attribute

When the URL is from the same source (same domain name, same protocol, same port number), in this case, use the a tag plus the download attribute. The download attribute instructs the browser to download the file instead of opening it. At the same time, the attribute value is File name when downloading;

3.2 Solution 2: The backend sets the response header Content-Disposition of the download request to force downloading

This is the most versatile method and is not affected by cross-domain and request methods.

Content-Disposition: attachment; filename=“filename.jpg”

If you want to use window.open to implement forced download, you can use this method

In a regular HTTP response, the value of this response header indicates the representation of the response content.

  • inline means displaying the response content as part of the page
  • attachment means downloading the response content as an attachment, and most browsers will present a "Save As" dialog box
  • filename (optional) Specifies the file name prefilled in the save box

3.3 Option 3: Make cross-domain requests through interfaces, dynamically create a tags, and download them in blob form

When the cross-domain problem of interface requests has been solved (such as Nginx method), you can get the file stream directly through the request, convert the file stream into blob format, and then download it through the download attribute of the a tag

3.3.1 fetch sends request
fetch('/upload/user.png').then((res) => {
    
    
  res.blob().then((blob) => {
    
    
    const blobUrl = window.URL.createObjectURL(blob);
    // 这里的文件名根据实际情况从响应头或者url里获取
    const filename = 'user.jpg';
    const a = document.createElement('a');
    a.href = blobUrl;
    a.download = filename;;
    a.click();
    window.URL.revokeObjectURL(blobUrl);
  });
});
  1. The above uses a native fetch request to dynamically generate an a tag to download the file.

  2. res.blob() This method is the response object method of the Fetch API. This method converts the file stream returned by the backend into a Promise that returns a blob; blob (Binary Large Object) is a binary type object that records original data information.

  3. URL.createObjectURL(blob) The return value of this method can be understood as a url pointing to the incoming parameter object. The object passed in the parameter can be accessed through this url.

  • What needs to be noted with this method is that even if the same object is passed in as a parameter, the url object returned each time is different.
  • The url object is stored in memory and will only be cleared when the current document (document) is unloaded. Therefore, for better performance, it needs to be actively released through URL.revokeObjectURL (blobUrl).
3.3.2 axios sends request
download(url: string, body: any, fileName: string, method?) {
    
    
	return axios({
    
    
	  method: method ||'post',
	  headers: {
    
    
	    'Content-Type': 'application/json; charset=utf-8'
	  },
	  url,
	  responseType: 'blob',
	  headers: {
    
     //如果需要权限下载的话,加在这里
	        Authorization: '123456'
	    }
	  data: JSON.stringify(params),
	  timeout: 60 * 1000
	}).then((res: any) => {
    
    
	   if (!res) {
    
    
	     message.error('下载失败')
	     return
	   }
	   console.log('res:', res)
	   let url = window.URL.createObjectURL(new Blob([res], {
    
     type: 'application/vnd.ms-excel' }))
	   let link = document.createElement('a')
	   link.style.display = 'none'
	   link.href = url
	   if (!fileName || typeof fileName != "string") {
    
    
	     fileName = "下载文件"
	   }
	   link.setAttribute('download', fileName + '.xlsx')
	   document.body.appendChild(link)
	   link.click()
	   document.body.removeChild(link); //下载完成移除元素
	   window.URL.revokeObjectURL(url); //释放掉blob对象
	 })
}
3.3.3 XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('GET', '/upload/user.png', true);
xhr.responseType = 'blob';
xhr.setRequestHeader('satoken', JSON.parse(sessionStorage.getItem('id_token'))); //设置请求头
xhr.onload = function() {
    
    
  if (this.status === 200) {
    
    
  const fileName = 'test.jpg';
    const blob = new Blob([this.response]);
    const blobUrl = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = blobUrl;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(blobUrl);
  }
};
xhr.send();
xhr.onreadystatechange = function () {
    
    
  if (xhr.readyState === 4 && xhr.status === 200) {
    
    
      // 成功后 做一些操作         
  }
};

3.4 Summary of the plan

  1. In non-cross-domain situations, add the download attribute to the a tag, such as < a href="url" download="xxx.png">< /a > Pay attention to the suffix when writing the file
    name in download (the value is not required)

  2. The method common to all situations: The backend sets the response header Content-Disposition of the download request: attachment; filename="filename.jpg"

    • attachment means to force the browser to download
    • filename is used to set the pre-filled file name in the download pop-up box.
  3. Solve cross-domain problems through requests. Dynamically create a tags. Download them in blob form. See the analysis below for details.

The browser identifies the data type through the MIME type (media type, usually called Multipurpose Internet Mail Extensions or MIME type, such as: image/jpeg application/pdf) in the request header Content-Type, and processes the corresponding data accordingly. Files such as images and text that can be opened directly by the browser are opened by default. In order to prevent the browser from opening the file directly, we need to do some processing;

  1. Request through fetch, axios, XMLHttpRequest. The main logic here is that when our request is successful, we will get the response of the response body. This response is the content we want to download, and then we convert it into a blob object, and then pass it through the URL .createObjectURL to create a url, and then download the file through the download attribute of the a tag.
  • blob

A Blob object represents an immutable, raw data file-like object. Its data can be read in text or binary format, or converted into a ReadableStream for data operations.

Blobs do not necessarily represent data in JavaScript's native format. The File interface is based on Blob, inheriting the functionality of blob and extending it to support files on the user's system.

The blob object is a new object in HTML5. Its function is to store binary data, such as pictures, videos, audios, etc. Its usage is as follows:

/**
 * @param {Array} array 二进制数据,是一个由ArrayBuffer, ArrayBufferView, Blob, DOMString 等对象构成的 Array ,或者其他类似对象的混合体,它将会被放进 Blob。DOMStrings 会被编码为 UTF-8。
 * @param {Object} options 配置项,是一个可选的BlobPropertyBag字典,它可能会指定如下两个属性:
 * @param {String} options.type 文件类型,它代表了将会被放入到 blob 中的数组内容的 MIME 类型。
 * @param {String} options.endings 用于指定包含行结束符\n的字符串如何被写入。默认为transparent,表示不会修改行结束符。还可以指定为native,表示会将\n转换为\r\n。
 */
const blob = new Blob([], {
    
     type: '' })

MIME knowledge points: MIME

Commonly used MIME types:

{ “.xls”, “application/vnd.ms-excel” },
{ “.xlsx”, “application/vnd.openxmlformats-officedocument.spreadsheetml.sheet” },
{ “.doc”, “application/msword” },
{ “.docx”, “application/vnd.openxmlformats-officedocument.wordprocessingml.document” },
{ “.zip”, “application/zip” },
{ “.json”, “application/json” },
{ “.jpeg”, “image/jpeg” },
{ “.jpg”, “image/jpeg” },
{ “.png”, “image/png” },
{ “.ppt”, “application/vnd.ms-powerpoint” },
{ “.pptx”, “application/vnd.openxmlformats-officedocument.presentationml.presentation” },
{ “.mp3”, “audio/mpeg” },
{ “.mp4”, “video/mp4” },

The main concern here is the type attribute. By default, the blob object does not have a type attribute, so the blob is an untyped blob. The file will not be damaged, but it cannot be recognized normally.

  • URL.createObjectURL

The URL.createObjectURL() static method creates a DOMString containing a URL representing the object given in the argument. The life cycle of this URL is bound to the document in the window in which it was created. This new URL object represents the specified File object or Blob object.

This method is used to create a URL. Its function is to convert a blob object into a URL. This URL can be used to download files or preview files. The code is as follows:

const url = URL.createObjectURL(blob)

What needs to be noted here is that the life cycle of this URL is bound to the document in the window in which it was created. That is to say, when our document is destroyed, this URL will become invalid, so we need to destroy it at the appropriate time. code show as below:

URL.revokeObjectURL(url)

Going back to the problem we just downloaded, we solved it through blob objects. If this interface is the interface for downloading files, the files may be of various types. How should we deal with it?

Get the file type through the response header:

const type = response.headers['content-type']
 
const blob = new Blob([response.data], {
    
     type })

content-type may also be any binary data application/octet-stream. In this case, we need to obtain the file type through file-type:

import {
    
    fileTypeFromStream} from 'file-type';
 
const type = await fileTypeFromStream(response.body);
const blob = new Blob([response.data], {
    
     type })

4. Attention

  1. When implemented using Scheme 1 or 2, the file name of the downloaded file is given priority to the filename of Content-Disposition instead of download. In the form of blob, the file name is given priority to the download attribute. If neither is set, the last section of the URL is taken.

  2. When you access a file through the interface form fetch('/upload/downloadfile') and want to retain the browser preview effect, you can only set the filename of Content-Disposition to specify the file name downloaded during preview, otherwise the browser will default to The last section of the URL, that is, downloadfile is the file name, resulting in the downloaded file being unable to be opened without a suffix.

  3. window.open() and the a tag perform the operation of opening a link, which is similar to entering the address directly into the browser, which is equivalent to jumping from one domain to another, so window.open('http://xxx' ) can be accessed without reporting a cross-domain error; fetch/xhr only sends requests from the current domain, so fetch('http://xxx') will report a cross-domain error.

  4. The browser’s priority for downloading file names is Content-Disposition: filename="filename.png" takes precedence over < a download="filename.png">< / a > and takes precedence over the last section of the URL http:// localhost:8087/upload/filename.png.

5. bolb file stream display

  • show pdf
var bl = new Blob([blob], {
    
    
   type: 'application/pdf;charset=UTF-8'
})
// var link = document.createElement('a');
// link.href = window.URL.createObjectURL(bl);
// link.target = '_blank';
// link.click();
var urrl = window.URL.createObjectURL(bl);
window.open(urrl)

Preview the jpg image and change the type in the new Blob above to: 'image/jpg;charset=UTF-8'

For other formats, please refer to: Rookie Tutorial MIME Type

  • Picture Preview
  1. Synchronously
var img = new Image()
img.src = URL.createObjectURL(file)
document.body.appendChild(img)
  1. asynchronous mode
var img = new Image()
var Reader = new FileReader()
Reader.readAsDataURL(file)
Reader.onload = function() {
    
    
	img.src = Reader.result
}
document.body.appendChild(img)

Guess you like

Origin blog.csdn.net/renlimin1/article/details/127231667