JS通过url下载文件并重命名(同域/跨域)

533 阅读3分钟

一、前言

两种实现文件下载的方法:

  • 1.在同域环境下利用a标签的download属性直接下载;
  • 2.通过获取blob对象实现跨域下载,并允许自定义文件名。
  • window.location.replace("juejin.cn/user/840368…")

二、同域下载

  • 由于a.download在跨域的情况下会失效,下列代码只可同域实现
/**
 * 创建一个虚拟的<a>标签用于下载文件(同域文件)
 * @param {string} url - 要下载的文件URL(可以是相对路径或绝对路径)
 * @param {string} [filename] - 可选,下载时保存的文件名(若不提供则使用服务器返回的文件名)
 */
function downloadFile(url, filename) {
  // 创建一个虚拟的<a>标签元素
  const a = document.createElement('a');
  // 设置<a>标签的href属性为要下载的文件URL
  a.href = url;
  // 设置download属性
  // 如果提供了filename参数,则使用它作为下载文件名
  a.download = filename || '';
  // 将<a>标签临时添加到DOM中(某些浏览器需要元素在DOM中才能触发点击)
  document.body.appendChild(a);
  // 模拟用户点击这个链接(触发文件下载)
  a.click();
  //下载完成后,从DOM中移除这个临时创建的<a>标签
  document.body.removeChild(a);
}

三、跨域下载

1.解析为Blob对象

/**
 * 通过XMLHttpRequest获取指定URL的Blob数据
 * @param {string} url - 要获取资源的URL
 * @returns {Promise<Blob>} 返回一个解析为Blob对象的Promise
 */
function getBlob(url) {
  return new Promise(resolve => {
    // 创建XMLHttpRequest对象
    const xhr = new XMLHttpRequest();
    // 初始化请求 (GET方法, 异步请求)
    xhr.open('GET', url, true);
    
    // 设置响应类型为'blob',这样xhr.response将会是Blob对象
    xhr.responseType = 'blob';
    
    // 设置请求完成时的回调函数
    xhr.onload = () => {
      // 当HTTP状态码为200时表示请求成功
      if(xhr.status === 200) {
        // 将Blob对象通过Promise resolve返回
        resolve(xhr.response);
      }
    };
    // 发送请求
    xhr.send();
  });
}

2.获取文件扩展名

// 获取文件扩展名
	function getUrlExtension(url) {
		// 步骤解析:
		// 1.使用正则表达式 /[#?]/ 分割 URL   例:"file.txt?param=1#section" → "file.txt"
		// 2.通过.分割数组   例:"file.txt" → ["file", "txt"]
		// 3.取数组最后一个元素
		// 4.去空
		return url.split(/[#?]/)[0].split('.').pop().trim();
	}

3.调用

getBlob(fileUrl).then(blob => {
			// 创建一个隐藏的<a>标签用于触发下载
			let link = document.createElement("a");
			// 隐藏链接,不显示在页面上
			link.style.display = 'none';
			// 设置下载文件名:自定义文件名 + 原始文件扩展名
			link.download = `${customizeFileName}.${getUrlExtension(fileUrl)}`;
			// 创建Blob对象的URL并设置为链接的href
			link.href = window.URL.createObjectURL(blob);
			// 将链接临时添加到DOM中(某些浏览器需要元素在DOM中才能触发点击)
			document.body.appendChild(link);
			// 模拟点击链接触发下载
			link.click();
			// 下载完成后移除临时链接
			document.body.removeChild(link);
		});

4.完整代码案例及注释

<!doctype html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<title>Document</title>
</head>
<body>
	<button>下载</button>
</body>
<script>
	let button = document.querySelector('button');
	// 自定义文件名
	let customizeFileName = '自定义文件名';
	// 文件url
	let fileUrl = 'https://so1.360tres.com/t0165f9b1a3dca6c254.jpg';
	/**
	 * 通过XMLHttpRequest获取指定URL的Blob数据
	 * @param {string} url - 要获取资源的URL
	 * @returns {Promise<Blob>} 返回一个解析为Blob对象的Promise
	 */
	function getBlob(url) {
	  return new Promise(resolve => {
	    // 创建XMLHttpRequest对象
	    const xhr = new XMLHttpRequest();
	    // 初始化请求 (GET方法, 异步请求)
	    xhr.open('GET', url, true);
	    // 设置响应类型为'blob',这样xhr.response将会是Blob对象
	    xhr.responseType = 'blob';
	    // 设置请求完成时的回调函数
	    xhr.onload = () => {
	      // 当HTTP状态码为200时表示请求成功
	      if(xhr.status === 200) {
	        // 将Blob对象通过Promise resolve返回
	        resolve(xhr.response);
	      }
	    };
	    // 发送请求
	    xhr.send();
	  });
	}
	
	// 获取文件扩展名
	function getUrlExtension(url) {
		// 步骤解析:
		// 1.使用正则表达式 /[#?]/ 分割 URL   例:"file.txt?param=1#section" → "file.txt"
		// 2.通过.分割数组   例:"file.txt" → ["file", "txt"]
		// 3.取数组最后一个元素
		// 4.去空
		return url.split(/[#?]/)[0].split('.').pop().trim();
	}
	
	button.addEventListener('click', function() {
		getBlob(fileUrl).then(blob => {
			// 创建一个隐藏的<a>标签用于触发下载
			let link = document.createElement("a");
			// 隐藏链接,不显示在页面上
			link.style.display = 'none';
			// 设置下载文件名:自定义文件名 + 原始文件扩展名
			link.download = `${customizeFileName}.${getUrlExtension(fileUrl)}`;
			// 创建Blob对象的URL并设置为链接的href
			link.href = window.URL.createObjectURL(blob);
			// 将链接临时添加到DOM中(某些浏览器需要元素在DOM中才能触发点击)
			document.body.appendChild(link);
			// 模拟点击链接触发下载
			link.click();
			// 下载完成后移除临时链接
			document.body.removeChild(link);
		});
	});
</script>
</html>