说明
- 页面内增加一个用于显示进度的标签 div.progress
- js 内处理增加进度处理的监听函数xhr.upload.onprogress
- event.lengthComputable这是一个状态,表示发送的长度有了变化,可计算
- event.loaded表示发送了多少字节
- event.total表示文件总大小
- 根据event.loaded和event.total计算进度,渲染div.progress
xhr.upload.onprogress要写在xhr.send方法前面,否则event.lengthComputable状态不会改变,只有在最后一次才能获得,也就是100%的时候.
HTML
<div>
选择文件(可多选):
<input type="file" id="f1" multiple/><br/><br/>
<div id="progress">
<span class="red"></span>
</div>
<button type="button" id="btn-submit">上 传</button>
</div>
复制代码
JS
<script>
function submitUpload() {
var progressSpan = document.getElementById('progress').firstElementChild;
var fileList = document.getElementById('f1').files;
progressSpan.style.width='0';
progressSpan.classList.remove('green');
if(!fileList.length){
alert('请选择文件');
return;
}
var fd = new FormData(); //构造FormData对象
fd.append('title', document.getElementById('title').value);
for(var i =0;i<fileList.length;i ){
fd.append('f1', fileList[i]);//支持多文件上传
}
var xhr = new XMLHttpRequest(); //创建对象
xhr.open('POST', 'http://10.70.65.235:8100/', true);
xhr.onreadystatechange = function () {
console.log('state change', xhr.readyState);
if (xhr.readyState == 4) {
var obj = JSON.parse(xhr.responseText); //返回值
console.log(obj);
if(obj.fileUrl.length){
//alert('上传成功');
}
}
}
xhr.onprogress=updateProgress;
xhr.upload.onprogress = updateProgress;
function updateProgress(event) {
console.log(event);
if (event.lengthComputable) {
var completedPercent = (event.loaded / event.total * 100).toFixed(2);
progressSpan.style.width= completedPercent '%';
progressSpan.innerHTML=completedPercent '%';
if(completedPercent>90){//进度条变色
progressSpan.classList.add('green');
}
console.log('已上传',completedPercent);
}
}
//注意 send 一定要写在最下面,否则 onprogress 只会执行最后一次 也就是100%的时候
xhr.send(fd);//发送时 Content-Type默认就是: multipart/form-data;
}
//绑定提交事件
document.getElementById('btn-submit').addEventListener('click',submitUpload);
</script>
复制代码
CODE
https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo
多文件上传 预览 取消上一个栗子的多文件上传只有一个进度条,有些需求可能会不大一样,需要观察到每个文件的上传进度,并且可以终止上传。
DEMO
说明
- 为了预览的需要,我们这里选择上传图片文件,其他类型的也一样,只是预览不方便
- 页面内增加一个多图预览的容器div.img-box
- 根据选择的文件信息动态创建所属的预览区域和进度条以及取消按钮
- 为取消按钮绑定事件,调用xhr.abort();终止上传
- 使用window.URL.createObjectURL预览图片,在图片加载成功后需要清除使用的内存window.URL.revokeObjectURL(this.src);
HTML
<div>
选择文件(可多选):
<div class="addfile">添加文件
<input type="file" id="f1" multiple />
</div>
<div class="img-box"></div>
<button type="button" id="btn-submit">上 传</button>
</div>
复制代码
JS
<script>
//更改网络 为慢3g,就可以比较明显的看到进度条了
var fileMaxCount=6;
var imgBox =document.getElementsByClassName('img-box')[0];
var willUploadFile=[];//保存待上传的文件以及相关附属信息
document.getElementById('f1').addEventListener('change',function (e) {
var fileList = document.getElementById('f1').files;
if (willUploadFile.length > fileMaxCount || fileList.length>fileMaxCount || (willUploadFile.length fileList.length>fileMaxCount)) {
alert('最多只能上传' fileMaxCount '张图');
return;
}
for (var i = 0; i < fileList.length; i ) {
var f = fileList[i];//先预览图片
var img = document.createElement('img');
var item = document.createElement('div');
var progress = document.createElement('div');
progress.className='progress';
progress.innerHTML = '<span class="red"></span><button type="button">Abort</button>';
item.className='item';
img.src = window.URL.createObjectURL(f);
img.onload = function () {
//显示要是否这块儿内存
window.URL.revokeObjectURL(this.src);
}
item.appendChild(img);
item.appendChild(progress);
imgBox.appendChild(item);
willUploadFile.push({
file:f,
item,
progress
});
}
});
function xhrSend({file, progress}) {
var progressSpan = progress.firstElementChild;
var btnCancel = progress.getElementsByTagName('button')[0];
var abortFn=function(){
if(xhr && xhr.readyState!==4){
//取消上传
xhr.abort();
}
}
btnCancel.removeEventListener('click',abortFn);
btnCancel.addEventListener('click',abortFn);
progressSpan.style.width='0';
progressSpan.classList.remove('green');
var fd = new FormData(); //构造FormData对象
fd.append('f1',file);
var xhr = new XMLHttpRequest(); //创建对象
xhr.open('POST', 'http://localhost:8100/', true);
xhr.onreadystatechange = function () {
console.log('state change', xhr.readyState);
//调用 abort 后,state 立即变成了4,并不会变成0
//增加自定义属性 xhr.uploaded
if (xhr.readyState == 4 && xhr.uploaded) {
var obj = JSON.parse(xhr.responseText); //返回值
console.log(obj);
if(obj.fileUrl.length){
//alert('上传成功');
}
}
}
xhr.onprogress=updateProgress;
xhr.upload.onprogress = updateProgress;
function updateProgress(event) {
if (event.lengthComputable) {
var completedPercent = (event.loaded / event.total * 100).toFixed(2);
progressSpan.style.width= completedPercent '%';
progressSpan.innerHTML=completedPercent '%';
if(completedPercent>90){//进度条变色
progressSpan.classList.add('green');
}
if(completedPercent>=100){
xhr.uploaded=true;
}
console.log('已上传',completedPercent);
}
}
//注意 send 一定要写在最下面,否则 onprogress 只会执行最后一次 也就是100%的时候
xhr.send(fd);//发送时 Content-Type默认就是: multipart/form-data;
return xhr;
}
//文件上传
function submitUpload(willFiles) {
if(!willFiles.length){
return;
}
//遍历文件信息进行上传
willFiles.forEach(function (item) {
xhrSend({
file:item.file,
progress:item.progress
});
});
}
//绑定提交事件
document.getElementById('btn-submit').addEventListener('click',function () {
submitUpload(willUploadFile);
});
</script>
复制代码
问题1
这里没有做上传的并发控制,可以通过控制同时可上传文件的个数(这里控制为最多6个)或者上传的时候做好并发处理,也就是同时只能上传 X 个文件。
问题2在测试过程中,取消请求的方法xhr.abort()调用后,xhr.readyState会立即变为4,而不是0,所以这里需要做容错处理。
MDN 上说是0.
如果大家有不同的结果,欢迎留言。
CODE
https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo
拖拽上传html5的出现,让拖拽上传交互成为可能,现在这样的体验也屡见不鲜。
DEMO
说明
- 定义一个允许拖放文件的区域div.drop-box
- 取消drop 事件的默认行为e.preventDefault();,不然浏览器会直接打开文件
- 为拖拽区域绑定事件,鼠标在拖拽区域上 dragover, 鼠标离开拖拽区域dragleave, 在拖拽区域上释放文件drop
- drop事件内获得文件信息e.dataTransfer.files
HTML
<div class="drop-box" id="drop-box">
拖动文件到这里,开始上传
</div>
<button type="button" id="btn-submit">上 传</button>
复制代码
JS
<script>
var box = document.getElementById('drop-box');
//禁用浏览器的拖放默认行为
document.addEventListener('drop',function (e) {
console.log('document drog');
e.preventDefault();
});
//设置拖拽事件
function openDropEvent() {
box.addEventListener("dragover",function (e) {
console.log('elemenet dragover');
box.classList.add('over');
e.preventDefault();
});
box.addEventListener("dragleave", function (e) {
console.log('elemenet dragleave');
box.classList.remove('over');
e.preventDefault();
});
box.addEventListener("drop", function (e) {
e.preventDefault(); //取消浏览器默认拖拽效果
var fileList = e.dataTransfer.files; //获取拖拽中的文件对象
var len=fileList.length;//用来获取文件的长度(其实是获得文件数量)
//检测是否是拖拽文件到页面的操作
if (!len) {
box.classList.remove('over');
return;
}
box.classList.add('over');
window.willUploadFileList=fileList;
}, false);
}
openDropEvent();
function submitUpload() {
var fileList = window.willUploadFileList||[];
if(!fileList.length){
alert('请选择文件');
return;
}
var fd = new FormData(); //构造FormData对象
for(var i =0;i<fileList.length;i ){
fd.append('f1', fileList[i]);//支持多文件上传
}
var xhr = new XMLHttpRequest(); //创建对象
xhr.open('POST', 'http://localhost:8100/', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
var obj = JSON.parse(xhr.responseText); //返回值
if(obj.fileUrl.length){
alert('上传成功');
}
}
}
xhr.send(fd);//发送
}
//绑定提交事件
document.getElementById('btn-submit').addEventListener('click',submitUpload);
</script>
复制代码
CODE
https://github.com/Bigerfe/fe-learn-code/tree/master/src/upfiles-demo
剪贴板上传掘金的写文编辑器是支持粘贴上传图片的,比如我从磁盘粘贴或者从网页上右键复制图片。
DEMO