El front-end implementa el método de descarga de archivos y el back-end devuelve la secuencia del archivo blob para su visualización.

Las descargas de front-end generalmente se dividen en dos situaciones: una es que el back-end proporciona directamente una dirección de archivo y se puede descargar abriéndolo a través de un navegador, la otra es que se debe enviar una solicitud y el back-end devuelve datos de flujo binario, y el front-end analiza los datos de flujo y genera una URL para lograr la descarga.

1. una etiqueta:

Descargar archivos a través del atributo de descarga de la etiqueta a es el método más simple y común. Veamos primero el código de muestra:

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

A través del ejemplo anterior, descubrimos que al hacer clic en descargar se salta a la página de inicio de Baidu sin descargar el archivo.

Debido a que la descarga de una etiqueta solo puede descargar archivos de la misma fuente, si se trata de un archivo de dominio cruzado , que incluye imágenes, audio y video y otros archivos multimedia, se obtiene una vista previa de ellos y no se puede descargar .

El código anterior implementa la descarga de archivos directamente escribiendo una etiqueta, también podemos implementarlo a través de js, el código es el siguiente:

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);
}

El efecto es el mismo que el anterior: salta a la página de inicio de Baidu sin descargar el archivo.

El foco aquí es el atributo de descarga de la etiqueta a, que es nuevo en HTML5.

Su función es especificar el nombre del archivo descargado. Si no se especifica, el nombre del archivo descargado se determinará en función de la disposición del contenido del contenido solicitado. Si no hay disposición del contenido, se utilizará la última parte de la URL solicitada. como nombre del archivo. .

  • ¿Qué es la disposición de contenido?

Content-Disposition se refiere a un parámetro en el encabezado de respuesta HTTP utilizado para controlar el comportamiento del cliente al guardar archivos. Este parámetro se puede configurar en "en línea" o "adjunto", el primero significa abrir el archivo directamente en el navegador y el segundo significa permitir que el navegador descargue el archivo.

Por ejemplo, Content-Disposition:attachment;filename="example.mp4" significa forzar al navegador a descargar el archivo example.mp4 en la respuesta HTTP actual. Content-Disposition:inline;filename="example.pdf" abrirá directamente el archivo example.pdf en la respuesta HTTP actual en el navegador.

  • El papel y el uso de la disposición de contenido.

Content-Disposition se utiliza principalmente para indicarle al navegador cómo manejar los archivos devueltos por el servidor. Cuando el servidor devuelve un archivo, el navegador decidirá cómo procesarlo según el tipo MIME del archivo de forma predeterminada. Sin embargo, si el servidor quiere que el navegador realice una acción específica, como descargar un archivo, puede utilizar Content-Disposition para indicarle al navegador qué acción debe realizar.

En aplicaciones prácticas, la disposición de contenido generalmente se configura en el lado del servidor en lugar de en el lado del cliente. Por ejemplo, en Spring Framework, puede configurar Content-Disposition para la respuesta agregando el siguiente código.

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

El código anterior indica que la disposición del contenido de la respuesta es "adjunto" y especifica que el nombre del archivo descargado es "miarchivo.pdf".

2. ventana abierta

El uso de la etiqueta a también se puede lograr a través de window.open. El efecto es el mismo. El código es el siguiente:

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

_blank significa abrir en una nueva página. Si no se especifica, se abrirá en la página actual de forma predeterminada; también se puede utilizar el atributo de descarga de la etiqueta a. Los archivos entre dominios tampoco se pueden descargar .

3. El backend proporciona una interfaz para descargar a través de secuencias de archivos.

3.1 Solución 1 una etiqueta + atributo de descarga

Cuando la URL proviene de la misma fuente (mismo nombre de dominio, mismo protocolo, mismo número de puerto), en este caso, use la etiqueta a más el atributo de descarga. El atributo de descarga le indica al navegador que descargue el archivo en lugar de abrirlo. al mismo tiempo, el valor del atributo es Nombre del archivo al descargar;

3.2 Solución 2: el backend establece el encabezado de respuesta Disposición de contenido de la solicitud de descarga para forzar la descarga

Este es el método más versátil y no se ve afectado por los métodos de solicitud y entre dominios.

Contenido-Disposición: adjunto; nombre de archivo = “nombre de archivo.jpg”

Si desea utilizar window.open para implementar la descarga forzada, puede utilizar este método

En una respuesta HTTP normal, el valor de este encabezado de respuesta indica la representación del contenido de la respuesta.

  • en línea significa mostrar el contenido de la respuesta como parte de la página
  • adjunto significa descargar el contenido de la respuesta como un archivo adjunto, y la mayoría de los navegadores presentarán un cuadro de diálogo "Guardar como".
  • nombre de archivo (opcional) Especifica el nombre del archivo precargado en el cuadro de guardar

3.3 Opción 3: realizar solicitudes entre dominios a través de interfaces, crear etiquetas dinámicamente y descargarlas en forma de blob

Cuando se haya resuelto el problema entre dominios de las solicitudes de interfaz (como el método Nginx), puede obtener la secuencia del archivo directamente a través de la solicitud, convertir la secuencia del archivo al formato blob y luego descargarlo a través del atributo de descarga de la etiqueta a.

3.3.1 solicitud de recuperación de envíos
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. Lo anterior utiliza una solicitud de recuperación nativa para generar dinámicamente una etiqueta para descargar el archivo.

  2. res.blob() Este método es el método de objeto de respuesta de Fetch API. Este método convierte el flujo de archivos devuelto por el backend en una Promesa que devuelve un blob;blob (Binary Large Object) es un objeto de tipo binario que registra datos originales información.

  3. URL.createObjectURL (blob) El valor de retorno de este método puede entenderse como una URL que apunta al objeto de parámetro entrante. Se puede acceder al objeto pasado en el parámetro a través de esta URL.

  • Lo que hay que tener en cuenta con este método es que incluso si se pasa el mismo objeto como parámetro, el objeto URL devuelto cada vez es diferente.
  • El objeto URL se almacena en la memoria y solo se borrará cuando se descargue el documento actual (documento), por lo que para un mejor rendimiento, debe liberarse activamente a través de URL.revokeObjectURL (blobUrl).
3.3.2 axios envía solicitud
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 Solicitud XMLHttp
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 Resumen del plan

  1. En situaciones que no sean entre dominios, agregue el atributo de descarga a la etiqueta a, como < a href="url" download="xxx.png">< /a > Preste atención al sufijo al escribir el nombre del archivo en la descarga
    . (el valor no es obligatorio)

  2. El método común a todas las situaciones: el backend establece el encabezado de respuesta Disposición de contenido de la solicitud de descarga: archivo adjunto; nombre de archivo="nombre de archivo.jpg"

    • archivo adjunto significa forzar la descarga del navegador
    • nombre de archivo se utiliza para establecer el nombre del archivo precargado en el cuadro emergente de descarga.
  3. Resuelva problemas entre dominios a través de solicitudes. Cree etiquetas dinámicamente. Descárguelas en forma de blob. Consulte el análisis a continuación para obtener más detalles.

El navegador identifica el tipo de datos a través del tipo MIME (tipo de medio, generalmente llamado Extensiones multipropósito de correo de Internet o tipo MIME, como: imagen/aplicación jpeg/pdf) en el encabezado de la solicitud Tipo de contenido y procesa los datos correspondientes en consecuencia. como imágenes y texto que el navegador puede abrir directamente se abren de forma predeterminada. Para evitar que el navegador abra el archivo directamente, debemos realizar algún procesamiento;

  1. Solicitud a través de fetch, axios, XMLHttpRequest. La lógica principal aquí es que cuando nuestra solicitud tiene éxito, obtendremos la respuesta del cuerpo de la respuesta. Esta respuesta es el contenido que queremos descargar y luego lo convertimos en un objeto blob. y luego páselo a través de la URL .createObjectURL para crear una URL y luego descargue el archivo a través del atributo de descarga de la etiqueta a.
  • gota

Un objeto Blob representa un objeto inmutable similar a un archivo de datos sin procesar. Sus datos se pueden leer en formato de texto o binario, o convertirse en un ReadableStream para operaciones de datos.

Los blobs no necesariamente representan datos en el formato nativo de JavaScript. La interfaz de Archivo se basa en Blob, hereda la funcionalidad de blob y la extiende para admitir archivos en el sistema del usuario.

El objeto blob es un objeto nuevo en HTML5, su función es almacenar datos binarios, como imágenes, videos, audios, etc., su uso es el siguiente:

/**
 * @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: '' })

Puntos de conocimiento MIME: MIME

Tipos MIME más utilizados:

{ “.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” } ,

La principal preocupación aquí es el atributo de tipo. De forma predeterminada, el objeto blob no tiene un atributo de tipo, por lo que el blob es un blob sin tipo. El archivo no se dañará, pero no se podrá reconocer normalmente.

  • URL.createObjectURL

El método estático URL.createObjectURL() crea un DOMString que contiene una URL que representa el objeto dado en el argumento. El ciclo de vida de esta URL está vinculado al documento en la ventana en la que se creó. Este nuevo objeto URL representa el objeto File o el objeto Blob especificado.

Este método se utiliza para crear una URL. Su función es convertir un objeto blob en una URL. Esta URL se puede utilizar para descargar archivos o obtener una vista previa de ellos. El código es el siguiente:

const url = URL.createObjectURL(blob)

Lo que hay que tener en cuenta aquí es que el ciclo de vida de esta URL está vinculado al documento en la ventana en la que se creó, es decir, cuando nuestro documento se destruya, esta URL dejará de ser válida, por lo que debemos destruirla. en el momento apropiado. El código se muestra a continuación:

URL.revokeObjectURL(url)

Volviendo al problema que acabamos de descargar, lo solucionamos mediante objetos blob, si esta interfaz es la interfaz para descargar archivos, los archivos pueden ser de varios tipos, ¿cómo debemos solucionarlo?

Obtenga el tipo de archivo a través del encabezado de respuesta:

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

El tipo de contenido también puede ser cualquier aplicación de datos binarios/flujo de octeto. En este caso, necesitamos obtener el tipo de archivo a través del tipo de archivo:

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

4. Atención

  1. Cuando se implementa utilizando el Esquema 1 o 2, el nombre del archivo descargado tiene prioridad sobre el nombre de archivo de Disposición de contenido en lugar de descargar. En forma de blob, el nombre del archivo tiene prioridad sobre el atributo de descarga. Si ninguno de los dos está configurado , se toma la última sección de la URL.

  2. Cuando accede a un archivo a través del formulario de interfaz fetch('/upload/downloadfile') y desea conservar el efecto de vista previa del navegador, solo puede configurar el nombre de archivo Content-Disposition para especificar el nombre del archivo descargado durante la vista previa; de lo contrario, el navegador El valor predeterminado es La última sección de la URL, es decir, archivodescargar es el nombre del archivo, lo que hace que el archivo descargado no se pueda abrir sin un sufijo.

  3. window.open() y la etiqueta a realizan la operación de abrir un enlace, que es similar a ingresar la dirección directamente en el navegador, lo que equivale a saltar de un dominio a otro, por lo que window.open('http:// xxx' ) se puede acceder sin informar un error entre dominios; fetch/xhr solo envía solicitudes desde el dominio actual, por lo que fetch('http://xxx') informará un error entre dominios.

  4. La prioridad del navegador para descargar nombres de archivos es Content-Disposition: filename="filename.png" tiene prioridad sobre < a download="filename.png">< / a > y tiene prioridad sobre la última sección de la URL http:// localhost:8087/upload/nombredearchivo.png.

5. visualización de flujo de archivos bolb

  • mostrar 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)

Obtenga una vista previa de la imagen jpg y cambie el tipo en el nuevo Blob de arriba a: 'image/jpg;charset=UTF-8'

Para otros formatos, consulte: Tutorial para novatos Tipo MIME

  • Vista previa de imagen
  1. Sincrónicamente
var img = new Image()
img.src = URL.createObjectURL(file)
document.body.appendChild(img)
  1. modo asíncrono
var img = new Image()
var Reader = new FileReader()
Reader.readAsDataURL(file)
Reader.onload = function() {
    
    
	img.src = Reader.result
}
document.body.appendChild(img)

Supongo que te gusta

Origin blog.csdn.net/renlimin1/article/details/127231667
Recomendado
Clasificación