上传下载文件方式总结
# 上传下载文件方式总结
# 下载的方式
# 直接下载的方式
# 1. 通过A标签打开文件
下载静态资源或者 api 返回文件流,对于异常处理没有什么要求的,都可以使用直接下载的方式。但是仅支持浏览器无法识别的文件。如果是浏览器支持的文件格式(如:txt、jpg、png、mp3),就不会触发下载,而是直接用浏览器打开文件。
<a href="/static/example.json" download="A.json">
# 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);
}
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();
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);
});
};
}
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)
2
3
4
5
一般后端后返回下面的头来告诉客户端,要下载的文件是二进制流的形式。
'Content-Type': 'application/octet-stream',// 告诉浏览器这是一个二进制文件
'Content-Disposition': 'attachment; filename=' + name// 告诉浏览器这是一个需要下载的文件
2
在常规的 HTTP 响应中,Content-Disposition 响应头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。
语法:
Content-Disposition: inline
Content-Disposition: attachment
Content-Disposition: attachment; filename="filename.jpg"
2
3
它的第一个参数可以是inline或者attachment。如果第一个参数是inline(默认值)则表示展示到网页中或者做为网页展示。如果第一个参数是attachment表示它应该被下载,大部分浏览器会出现一个保存为的对话框,如果提供了filename会用filename填充对话框的文件名。
Content-Disposition: attachment; filename="filename.jpg"
Content-Disposition可以触发“保存为”的对话框
200 OK
Content-Type: text/html; charset=utf-8
Content-Disposition: attachment; filename="cool.html"
Content-Length: 22
<HTML>Save me!</HTML>
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--
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
这样,前端就能获取到对应暴露的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>
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 && 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();
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);
});
}
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);
})
};
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>
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);
}
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>
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