Glittering's blog Glittering's blog
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)

Glitz Ma

前端开发工程师
Home
  • 学习手册

    • 《JavaScript教程》
    • 《ES6 教程》
    • 《TypeScript 从零实现 axios》
    • 《Git》
    • TypeScript
  • 技术文档
  • 算法
  • 工作总结
  • 实用技巧
  • collect
About
  • Classification
  • Label
GitHub (opens new window)
  • 技术文档

  • 算法

  • 工作总结

    • 时区校正
    • 上传下载文件方式总结
      • 下载的方式
        • 直接下载的方式
        • 1. 通过A标签打开文件
        • 2. 通过js生成A标签下载文件衍生版本
        • 3. 使用js直接下载
        • 文件流转blob对象下载
      • 上传文件的方式
        • 1. 传统上传方式
        • 2. formData上传
        • 3. 拖拽上传
    • webpack优化实践
    • webpack基础应用知识总结
    • vue常见原理总结
    • vue基础知识总结
    • react高级特性
    • react基础知识总结
    • 微前端总结
    • 今天总结一下用到的css吧
    • 地图标绘--射线法来计算点在多边形内
    • web异常监控和分析
    • 工作中遇到的css问题记录
    • axios 与 promise
    • 前端优化指南
    • 小程序笔记
    • JS随机打乱数组
    • 非常实用的JavaScript一行代码
  • 实用技巧

  • 收藏夹

  • 技术
  • 工作总结
mamingjuan
2023-03-23
目录

上传下载文件方式总结

# 上传下载文件方式总结

# 下载的方式

# 直接下载的方式

# 1. 通过A标签打开文件

下载静态资源或者 api 返回文件流,对于异常处理没有什么要求的,都可以使用直接下载的方式。但是仅支持浏览器无法识别的文件。如果是浏览器支持的文件格式(如:txt、jpg、png、mp3),就不会触发下载,而是直接用浏览器打开文件。

<a href="/static/example.json" download="A.json">
1

# 2. 通过js生成A标签下载文件衍生版本

通过创建A标签实现下载,但这里顺便提到了通过blob方式下载以及ie浏览器下的兼容下载。

/**
 * 如果传递了url通过url下载,也可通过blob下载文件
 * 传递的blob对象 eg. var blob = new Blob([content]);
 */
function download(url = '', fileName = '未知文件', blob) {
    let href = ''
    if (url) {
      href = url
    } else {
      href = window.URL.createObjectURL(blob || ''); // 创建下载的链接
    }
    // IE11 & Edge   
    // IE10+
    if (navigator.msSaveBlob && blob) { // IE hack; see http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
      navigator.msSaveBlob(blob, filename)
      return false;
    }

    // ieVersion < 10
    // ieVersion now returns a boolean for the
    // sake of sanity. We just check `msSaveBlob` first.
    if (ieVersion && ieVersion < 10) {
      var frame = D.createElement('iframe');
      document.body.appendChild(frame);

      frame.contentWindow.document.open("text/html", "replace");
      frame.contentWindow.document.write(blob);
      frame.contentWindow.document.close();
      frame.contentWindow.focus();
      frame.contentWindow.document.execCommand('SaveAs', true, fileName);

      document.body.removeChild(frame);
      return true;
    }

    const a = document.createElement('a');
    a.style.display = 'none';
    a.setAttribute('target', '_blank');
    /*
      * download的属性是HTML5新增的属性
      * href属性的地址必须是非跨域的地址,如果引用的是第三方的网站或者说是前后端分离的项目(调用后台的接口),这时download就会不起作用。
      * 如果是下载浏览器无法解析的文件,例如.exe,.xlsx..那么浏览器会自动下载,但是如果使用浏览器可以解析的文件,比如.txt,.png,.pdf....浏览器就会采取预览模式
      * 所以,对于.txt,.png,.pdf等的预览功能我们就可以直接不设置download属性(前提是后端响应头的Content-Type: application/octet-stream,如果为application/pdf浏览器则会判断文件为 pdf ,自动执行预览的策略)
      */
    fileName && a.setAttribute('download', fileName);
    a.href = href;
    // 如果想设置编码格式的话,可以这样设置 a.setAttribute('href','data:text/plain;charset=utf-8, ' + encodeURIComponent(textInput)); 请参考 https://www.jiyik.com/tm/xwzj/web_3052.html 或者 https://www.twle.cn/t/19247
    document.body.appendChild(a);
    blob && URL.revokeObjectURL(a.href) // 释放URL 对象
    a.click(); // IE: "Access is denied"; see: https://connect.microsoft.com/IE/feedback/details/797361/ie-10-treats-blob-url-as-cross-origin-and-denies-access
    document.body.removeChild(a);
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

也可以用form形式下载文件

let myForm = document.createElement('form')
myForm.style.display = 'none'
myForm.setAttribute('target', '')
myForm.setAttribute('method', 'get')
myForm.setAttribute('action', url)
document.body.append(myForm);
myForm.submit();
1
2
3
4
5
6
7

顺便一提,下载想要的规格的图片

function downloadImgURL(url, name) { 
  let image = new Image(); 
  image.setAttribute("crossOrigin", "anonymous"); 
  image.src = url; 
  image.onload = () => { 
    let canvas = document.createElement("canvas"); 
    canvas.width = image.width; 
    canvas.height = image.height; 
    let ctx = canvas.getContext("2d"); 
    ctx.drawImage(image, 0, 0, image.width, image.height); 
    canvas.toBlob(blob => { 
      let url = URL.createObjectURL(blob); 
      let a = document.createElement("a"); 
      a.download = name; 
      a.href = url; 
      a.click(); 
      a.remove(); // 用完释放URL对象 
      URL.revokeObjectURL(url); 
      }); 
    }; 
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 3. 使用js直接下载

对于浏览器可以识别的文件格式(txt、jpg、png、mp3),可以通过设置 Content-Disposition,告诉浏览器资源应该被下载到本地。

const URL = '/static/example.json'
// 方式1
window.location.href = URL;
// 方式2
window.open(URL)
1
2
3
4
5

一般后端后返回下面的头来告诉客户端,要下载的文件是二进制流的形式。

'Content-Type': 'application/octet-stream',// 告诉浏览器这是一个二进制文件
'Content-Disposition': 'attachment; filename=' + name// 告诉浏览器这是一个需要下载的文件
1
2

在常规的 HTTP 响应中,Content-Disposition 响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。

语法:

Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"
1
2
3

它的第一个参数可以是inline或者attachment。如果第一个参数是inline(默认值)则表示展示到网页中或者做为网页展示。如果第一个参数是attachment表示它应该被下载,大部分浏览器会出现一个保存为的对话框,如果提供了filename会用filename填充对话框的文件名。

Content-Disposition: attachment; filename="filename.jpg"
1

Content-Disposition可以触发“保存为”的对话框

200 OK
Content-Type: text/html; charset=utf-8
Content-Disposition: attachment; filename="cool.html"
Content-Length: 22

<HTML>Save me!</HTML>
1
2
3
4
5
6

上面的响应,不会做为简单的html处理,大多数浏览器会企图以cool.html保存这个文件

在HTML页面中通过POST请求发送Content-Type:multipart/form-data,下面是个例子

POST /test.html HTTP/1.1
Host: example.org
Content-Type: multipart/form-data;boundary="boundary"

--boundary
Content-Disposition: form-data; name="field1"

value1
--boundary
Content-Disposition: form-data; name="field2"; filename="example.txt"

value2
--boundary--

1
2
3
4
5
6
7
8
9
10
11
12
13
14

关于如何自定义header,大家还是要知道一下的。

本质上跟上述的Content-Disposition差不多,只是我们这里不使用默认的header,我们自己自定义一个response header,跟后端决定好编码方式返回,前端直接获取这个自定义header,然后使用对应的解码即可,如使用decodeURIComponent。 但是我们都要知道,在跨域的情况下,前端获取到的header只有默认的6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。 所以你想要获取到别的header,需要后端配合,设置

Access-Control-Expose-Headers: Content-Disposition, custom-header
1

这样,前端就能获取到对应暴露的header字段,需要注意的是,Content-Disposition也是需要暴露的。

# 文件流转blob对象下载

虽说是讲blob下载,其实主要是讲怎么通过请求获取数据。请求接口的方式可以通过在header中携带token等方式,方便做鉴权操作。

方式一,通过axios请求接口

 <button onclick="download()">文件流转blob对象下载</button>
 <script>
 	download() {
 		axios({
 			url: 'http://www.xxx.com/download',
 			method: 'get',
 			responseType: 'blob',
 		}).then(res => {
 			const fileName = res.headers.content-disposition.split(';')[1].split('filename=')[1];
 			const filestream = res.data;  // 返回的文件流
 			// {type: 'application/vnd.ms-excel'}指定对应文件类型为.XLS (.XLS的缩写就为application/vnd.ms-excel)
 			const blob = new Blob([filestream], {type: 'application/vnd.ms-excel'});
 			const a = document.createElement('a');
 			const href = window.URL.createObjectURL(blob); // 创建下载连接
		    a.href = href;
		    a.download = decodeURL(fileName );
		    document.body.appendChild(a);
	        a.click();
	        document.body.removeChild(a); // 下载完移除元素
	        window.URL.revokeObjectURL(href); // 释放掉blob对象
 		})
 	}
 </script>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

方式二、通过XMLHttpRequest下载

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onprogress = function (event) {
    console.log(Math.round(event.loaded / event.total * 100) + "%");
};
xhr.responseType = 'arraybuffer';
xhr.addEventListener('readystatechange', function (event) {
    if (xhr.status === 200 &amp;&amp; xhr.readyState === 4) {
        // 获取响应头主要获取附件名称
        var contentDisposition = xhr.getResponseHeader('content-disposition');
        // 获取类型类型和编码
        var contentType = xhr.getResponseHeader('content-type');
        // 构造blob对象,具体看头部提供的链接地址
        var blob = new Blob([xhr.response], {
            type: contentType
        });
        var url = window.URL.createObjectURL(blob);
        // 获取文件夹名
        var regex = /filename=[^;]*/;
        var matchs = contentDisposition.match(regex);
        if (matchs) {
            filename = decodeURIComponent(matchs[0].split("=")[1]);
        } else {
            filename = +Date.now() + ".doc";
        }
        var a = document.createElement('a');
        a.href = url;
        a.download = filename;
        a.click();
        window.URL.revokeObjectURL(url);
        // dosomething
    }
})
xhr.send();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

方式三、通过fetch方式下载

function downloadFile(url, fileName) {
    fetch(url, {
        method: "get",
        mode: "no-cors",
        referrerPolicy: "no-referrer",
    })
      .then((res) => res.blob())
      .then((res) => {
          const aElement = document.createElement("a");
          aElement.setAttribute("download", fileName);
          const href = URL.createObjectURL(res);
          aElement.href = href;
          aElement.setAttribute("target", "_blank");
          aElement.click();
          URL.revokeObjectURL(href);
      });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

方式四、通过FileReader文件流方式

function (formData, url, name) {
  return new Promise((resolve, reject) => {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", url, true); // 也可以使用POST方式,根据接口
    xhr.responseType = "blob"; // 返回类型blob
    // 定义请求完成的处理函数,请求前也可以增加加载框/禁用下载按钮逻辑
    xhr.onload = function () {
      // 请求完成
      if (this.status === 200) {
        // 返回200
        var blob = this.response;
        var reader = new FileReader();
        reader.readAsDataURL(blob); // 转换为base64,可以直接放入a表情href
        reader.onload = function (e) {
          // 转换完成,创建一个a标签用于下载
          var a = document.createElement("a");
          a.download = name + ".xls";
          a.href = e.target.result;
          $("body").append(a); // 修复firefox中无法触发click
          a.click();
          resolve(200)
          $(a).remove();
        };
      }
    };
    // 发送ajax请求
    xhr.send(formData);
  })
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 上传文件的方式

# 1. 传统上传方式

<form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data" >
   <input type="file" id="upload" name="upload" /> <br />
   <input type="submit" value="Upload" />
</form>
1
2
3
4

# 2. formData上传

// 检查是否支持FormData
if(window.FormData) { 
  var formData = new FormData();

  // 建立一个upload表单项,值为上传的文件
  formData.append('upload', document.getElementById('upload').files[0]);
  var xhr = new XMLHttpRequest();
  xhr.open('POST', $(this).attr('action'));
  // 定义上传完成后的回调函数
  xhr.onload = function () {
    if (xhr.status === 200) {
      console.log('上传成功');
    } else {
      console.log('出错了');
    }
  };
  xhr.send(formData);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

关于formData不得不说一下,formData.append的参数只有三个,当设置value时只支持字符串类型和Blob类型

The field's value. This can be a string or Blob (including subclasses such as File). If none of these are specified the value is converted to a string.

# 3. 拖拽上传

拖放文件的代码,主要是定义dragover、dragend和drop这三个事件。

<div id="holder"></div>

<style>
  #holder {
  border: 10px dashed #ccc;
  width: 300px;
  min-height: 300px;
  margin: 20px auto;
  }

  #holder.hover {
    border: 10px dashed #0c0;
  }
</style>
<script>
  // 检查浏览器是否支持拖放上传。
if('draggable' in document.createElement('span')){
  var holder = document.getElementById('holder');
  holder.ondragover = function () { this.className = 'hover'; return false; };
  holder.ondragend = function () { this.className = ''; return false; };
  holder.ondrop = function (event) {
    event.preventDefault();
    this.className = '';
    var files = event.dataTransfer.files;
    // do something with files
  };
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

参考连接:

https://juejin.cn/post/6895599073676492807

https://www.jianshu.com/p/7636d5c60a8d

上次更新: 2025/04/07, 01:42:58
时区校正
webpack优化实践

← 时区校正 webpack优化实践→

Copyright © 2015-2025 Glitz Ma
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式