1、大文件的分块上传
1.1、背景:如何优化文件上传性能
- 上传前,压缩文件,从而缩小文件体积,减少上传流量。(
canvas来压缩图片
) - 针对许多 小图片合并后统一上传,减少请求数来优化。(
比如说小图标,通过canvas合并生成新的图片,只上传一次
),
针对其它文件的话,可以通过new FileReader()创建文件对象,再调用文件对象的readAsArrayBuffer(file)
方法读取文件内容,合并后转换成blob发送服务器。 - 针对大型文件分块上传
1.2、为什么要采用分块上传
如果上传的文件是一个大文件,本来上传时间就相对较久,如果再加上网络不稳定各种因素影响,容易导致传输中断,而重新上传的话又需要从0开始。而采用分片上传可以很好地解决这个问题。
1.3、分片上传的前后端原理说明
本质就是js通过
文件对象的slice方法
进行文件的分割(就类似字符串的substring方法),把大文件分割成多个小文件,再把这些小文件一个一个单独上传上去就可以了。后端就需要将这些小文件合并形成一个完整的文件。
前端
前端大文件分片上传,核心是利用Blob.prototype.slice
方法。调用这个slice方法可以返回原文件的某个切片。
这样我们就可以根据预先设置好的切片最大数量将文件切分为一个个切片
,然后借助http的可并发性,同时上传多个切片
,这样从原本传一个大文件就变成了同时传多个小的文件切片,大大减少上传时间。
如何并发请求?
通过使用异步Promise.All
或者web worker
另外由于是并发,传输到服务端的顺序可能会发生变化,所以我们还需要给每个切片记录顺序。
//前端分割代码
var file = document.getElementById("file").files[0];
var chunk = file.slice(start,end);//切割文件
服务端
服务端需要负责接受这些切片,并将接收到的所有切片合并成原来的文件。
这里又引伸出两个问题:
1、何时合并切片,即切片什么时候传输完成;2、如何合并切片
第一个问题需要前端进行配合,前端在每个切片中都携带切片最大数量的信息,当服务端接受到的这个数量的切片时,自动合并,也可以额外发一个请求主动通知服务端进行切片的合并;
第二个问题,具体如何合并切片呢? 比如说使用 nodejs的 api ,fs.appendFileSync
,它可以同步地将数据追加到指定文件,也就是说,当服务端接受到所有切片后,先创建一个最终的文件,然后将所有切片逐步合并到这个文件中
1.4、分块上传还有哪些优势
- 更强容错能力
- 可以模拟暂停与继续
- 更精准的上传速度跟踪
分块也会有一定的副作用,本来是一个请求,分块后变成了多个请求,自然会带来网络开销
1.5、断点续传
断点续传是基于 分块上传 的功能上,在已经上传的分片上,继续上传剩下的分块文件
1.6、分块上传的前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>upload</title>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<input type="file" name="file" id="file">
<button id="upload" onClick="upload()">upload</button>
<script type="text/javascript">
var bytesPerPiece = 1024 * 1024; // 每个文件切片大小定为1MB .
var totalPieces;
//发送请求
function upload() {
var blob = document.getElementById("file").files[0];
var start = 0;
var end;
var index = 0;
var filesize = blob.size;
var filename = blob.name;
//计算文件切片总数
totalPieces = Math.ceil(filesize / bytesPerPiece);
while(start < filesize) {
end = start + bytesPerPiece;
if(end > filesize) {
end = filesize;
}
var chunk = blob.slice(start,end);//切割文件
var sliceIndex= blob.name + index;
var formData = new FormData();
formData.append("file", chunk, filename);
$.ajax({
url: 'http://localhost:9999/test.php',
type: 'POST',
cache: false,
data: formData,
processData: false,
contentType: false,
}).done(function(res){
}).fail(function(res) {
});
start = end;
index++;
}
}
</script>
</body>
</html>
分块上传的参考链接:https://www.cnblogs.com/sghy/p/9143955.html
2、常规文件上传的几种方式
不论是XMLHttpRequest、ajax还是form表单,总体思路如下:
- html中,选择上传的文件,必须通过
input[type="file"]
标签实现。- 可以通过获取
input[type="file"]
标签的dom对象中的files属性
来获取文件- 创建formdata对象,通过
append方法将文件添加formdata对象
中- 将formdata对象作为参数
发送服务器
。
var inputDOM = document.getElementById('file') //1、 获取input[type="file"] 对象
var file = inputDOM.files[0] // 2、获取指定文件
var formData = new FormData() // 3、创建formData对象,
formData.append('file', file) // 4、将文件添加到formData对象里
// 5、调用ajax请求,注意Content-Type请求头为false,processData也为false
$.ajax({
url: "请求地址",
type: "post",
data: formData,
processData: false, // 注意:使数据不做处理
contentType: false, // 注意:不要设置Content-Type请求头
success: function(res){ alert("上传成功!");},
error:function(err){ alert(err) }
})
2.1、直接通过form表单上传文件
2.2、原生XMLHttpRequest方法实现文件上传并监听上传进度
//1、首先监听input框的变动,选中一个新的文件会触发change事件
document.querySelector("#file").addEventListener("change",function(){
//2、获取到选中的文件
var file = document.querySelector("#file").files[0];
//3、创建formdata对象
var formdata = new FormData();
//4、调用formdata对象的append方法,添加参数
formdata.append("file",file);
//5、创建xhr,
var xhr = new XMLHttpRequest();
//6、配制请求地址,请求方法
xhr.open("post","/");
//7、xhr.onreadystatechange,监听请求状态变化,
xhr.onreadystatechange = function () {
if (xhr.readyState==4 && xhr.status==200){
// 请求成功了
}
}
//7.1、xhr.upload.onprogress 获取上传的进度
xhr.upload.onprogress = function (event) {
if(event.lengthComputable){
var percent = event.loaded/event.total *100;
console.log('已经上传了'+percent+'%');
}
}
//8、发送请求,将formdata上传
xhr.send(formdata);
});
2.3、ajax方式上传文件
<input type="file" name="file" id="file" />
<input type="button" value="提交" onclick="uploadFile();" />
function uploadFile() {
// 1、创建formdata对象
var formData = new FormData();
// 2、获取文件内容,并添加入formdata对象中
var file = $('#file')[0].files[0];
formData.append("file",file);
// 3、发送ajax请求
$.ajax({
url: "请求地址",
type: "post",
dataType:'json',
data: formData,
processData: false, // 注意:使数据不做处理
contentType: false, // 注意:不要设置Content-Type请求头
success: function(res) {
alert("上传成功!");
},
error: function(err) {
alert("上传失败失!", err);
}
})
};
3、限制文件上传大小
var input = document.getElementById('files'),
f = input.files[0];
//获取图片体积大小,默认单位是字节,除以(1024*1024)转化M,
var size = (f.size/(1024*1024)).toFixed(2);
f.size 获取的值,表示文件大小,以字节为单位;
1G=1024MB 1MB=1024KB 1KB=1024字节
4、文件上传前显示预览图
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
</head>
<style>
#imgPreview {
width: 40%;
height: 180px;
margin: 10px auto 0px auto;
border: 1px solid black;
text-align: center;
}
#prompt3 {
width: 100%;
height: 180px;
text-align: center;
position: relative;
}
#imgSpan {
position: absolute;
top: 60px;
left: 40px;
}
.filepath {
width: 100%;
height: 100%;
opacity: 0;
}
#img3 {
height: 100%;
width: 100%;
display: none;
}
</style>
<body>
<div id="imgPreview">
<div id="prompt3">
<span id="imgSpan">点击上传 <br />
<i class="aui-iconfont aui-icon-plus"></i>
<!--AUI框架中的图标-->
</span>
<input type="file" id="file" class="filepath" onchange="previewFile()" accept="image/jpg,image/jpeg,image/png,image/PNG">
<!--当vaule值改变时执行changepic函数,accept规定上传的文件类型,这里限定的是图片-->
</div>
<img src="#" id="img3" />
</div>
</body>
<script type="text/javascript" src="js/jquery-3.2.1.min.js"></script>
<script>
// 点击input,选中文件后,触发onchange事件。
// 方法一 文件预览的实现方法
//1、window.FileReader 创建一个文件阅读器,
//2、通过调用文件阅读器的 readAsDataURL(Blob/file)方法,读取指定的 Blob 或 File 对象,并用onload监听读取操作。
//3、当读取操作完成的时候,并触发 onload 事件,返回结果中将包含一个base64编码的字符串,以表示所读取文件的内容。(如果文件是图片的话,base64字符可以直接当作链接,用作图片的预览)
function previewFile(){
var reads = new FileReader();
f = document.getElementById('file').files[0];
reads.readAsDataURL(f);
reads.onload = function() {
document.getElementById('img3').src = this.result;
};
}
// 方法二 文件预览的实现方法
// window.URL.createObjectURL本地预览 ===> 由于兼容问题,不建议使用 **/
function previewFile02(){
var f = document.getElementById('file').files[0];
console.log(f);
var src =window.URL.createObjectURL(f);
console.log(src);
document.getElementById('img3').src = src;
}
// 方法三 文件上传后,后端返回文件地址以供访问
function previewFile03(){
var name = $("#file").val();
var formData = new FormData();
formData.append("name", name);
formData.append('file', $("#file").files[0]); /*获取上传的图片对象*/
$.ajax({
url: '/upload_avatar/',
type: 'POST',
data: formData,
processData: false, //必须---使数据不做处理
contentType: false, //必须---不设置Content-Type请求头
success: function(args) {
console.log(args); /*服务器端的图片地址*/
$("#img3").attr('src',args); /*预览图片*/
}
})
}
</script>
</html>
详细code,见github: https://github.com/yiyueqinghui/uploadImg
参考链接:https://www.cnblogs.com/wfblog/p/12801504.html