文件地址下载

a 标签下载(推荐)

Content-Disposition 实现文件下载

在服务端设置 Content-Disposition,其属性设置为 attachment; filename=‘123.jpg’,其中 attchment 表示以附件的形式下载并保存到本地;filename 为下载后的文件名。

// Midway.js 中设置响应头
 
this.ctx.set({
  "Content-Type": "image/jpeg",
  "Cache-Control": "no-cache",
  "Content-Disposition": "attachment; filename=123.jpeg"
});

Content-Disposition 的实现方式需要服务端配合,那么有没有其它的方式,在客户端即可实现文件的下载呢?其中的一种实现方式是使用 a 标签的 download 属性。

download 属性实现文件下载

在 a 标签中添加 download 属性:

<a href="123.jpg" download>下载</a>

这样就可以直接调用浏览器的下载功能,而且还可以给download属性设置值来规定下载文件的名称,如果在设置值时没有指定文件的扩展名,浏览器将自动检测正确的文件名并添加到文件(.jpg, .pdf, .txt, .html, 等等)。

<a href="123.jpg" download='othername.jpg'>下载</a>

download 是 HTML5 中新增的属性,因此必定会存在兼容性的问题:

然而,caniuse 展示的兼容性只是一个笼统,download 表现最为直观的一个问题就是跨域的问题。如果是加载了非同源的内容,该属性将会失效,等效于导航功能。

如果需要下载的资源是跨域的,包括跨子域,在Chrome浏览器下,使用download属性是可以下载的,但是,并不能重置下载的文件的命名;而FireFox浏览器下,则download属性是无效的,也就是FireFox浏览器无论如何都不支持跨域资源的download属性下载。

如果资源是同域名的,则两个浏览器都是畅通无阻的下载,不会出现下载变浏览的情况。

download 属性有兼容性问题,因此这并不是一个完美的解决方案,那么在客户端还有没有其它的方式实现文件的下载呢?

当然是有的,我们可以使用 URL.createObjectURL() 创建一个非跨域的数据源。

URL.createObjectURL() 实现文件下载

使用a标签进行下载,可以对文件进行命名且不会触发浏览器的保护机制。推荐使用a 标签进行下载。

/**
 * 下载文件
 * @param {*} url
 * @param {*} fileName
 */
export const download = (url, fileName = url) => {
  const a = document.createElement("a");
  a.download = fileName;
  a.href = url;
  a.target = "_blank";
  a.style.display = "none";
  document.body.appendChild(a);
  a.click();
};

注意❗

href 的下载地址 和 当前网站地址 必须是 同源的,否则download不生效。

下面是使用 XMLHttpRequest 进行优化。

/**
 * 下载文件
 * @param {*} url
 * @param {*} fileName
 */
const download = (url: string, fileName = url) => {
	const x = new window.XMLHttpRequest();
	x.open('GET', url, true);
	x.responseType = 'blob';
	x.onload = () => {
		const url = window.URL.createObjectURL(x.response);
		const a = document.createElement('a');
		a.href = url;
		a.download = fileName;
		a.click();
	};
	x.send();
};

使用方式:

download(url, '题目导入模板.xlsx');

form标签下载

/**
 * 下载文件
 * @param {String} path - 请求的地址
 * @param {String} fileName - 文件名
 */
function downloadFile (downloadUrl, fileName) {
    // 创建表单
    const formObj = document.createElement('form');
    formObj.action = downloadUrl;
    formObj.method = 'get';
    formObj.style.display = 'none';
    // 创建input,主要是起传参作用
    const formItem = document.createElement('input');
    formItem.value = fileName; // 传参的值
    formItem.name = 'fileName'; // 传参的字段名
    // 插入到网页中
    formObj.appendChild(formItem);
    document.body.appendChild(formObj);
    formObj.submit(); // 发送请求
    document.body.removeChild(formObj); // 发送完清除掉
}

window.open 下载(不推荐)

这种方法简单,但不能对文件进行重命名,并且会触发浏览器保护机制。

 window.open(url);

二进制流下载

content-type:application/octet-stream 下载

const res = await getAPI(QuestionGroupJwApi).apiQuestionGroupJwExportQuestionBasePost({
    responseType: 'blob',
});
const blob = new Blob([res.data]);
const url = URL.createObjectURL(blob);
download(url, '题目导入模板.xlsx');

扩展阅读