文件上传的核心就是 FileUpload 对象。在HTML文档中
<input type='file'>
每出现一次,一个FileUpload对象就会被创建。该对象包含的 value 属性保存了用户上传的文件名,并且该值是只读的,不可以被前端所修改的,当用户上传一个文件时,会触发 onchange 事件,可以以此当作钩子来操作。
在开始的时候,大家写 文件上传 功能的时候,会采用这种方法:
<form action='Upload.php' method='POST' enctype='multipart/form-data'>
<input type='file' name='file'></input>
<button type='submit'>提交</button>
</form>
但是这样的弊端就是上传后被刷新了!!!
现代前端都是基于Ajax进行文件上传的
还记得Ajax怎么写么?
var xml = new XMLHttpReques();
xml.open('POST', 'Upload.php');
xml.setRequestHeader('Content-Type', 'multipart/form-data');
xml.send(data);
xml.onchange = function(){
if(xml.readyState === 4 && xml.status === 200){
console.log('success');
}else{
console.log('fail');
}
}
以上是用level1版本,现在level2版本了。
这个版本:
1,可以支持文件上传了,
2,提供了进度提示,
3,可以设置超时处理。
var xhr = new XMLHttpRequest();
var formData = new FormData();
var fileInput = $("input[type='file']");
var file = fileInput.files[0];
// 第一个参数是 属性名 第二个参数是 属性值
formData.append('myFile', file);
xhr.open('POST', 'Upload.php');
// 这个onload相当于level1中的 xml.readyState === 4
xhr.onload = function(){
if(this.status === 200){
console.log('success')
}
}
xhr.send(formData);
xhr = null;
这样就可以通过ajax来无刷新上传文件了。
如果我们有对监听上传的进度的需求,那么需要拿到 upload 属性下面的 onprogress 方法。
onprogress 的事件回调方法中可以监听文件上传的进度,因为event下面有 loaded 和 total 属性, 前者是已上传,后者是文件总大小。 那么通过 event.loaded / event.total * 100 来拿到上传进度。
那么如何在前端展现出这个进度条呢?
HTML5为我们提供了一个 progress 标签。
<progress value='10' max='100'>
</progress>
这个标签是有默认样式的,如果想自定义样式,可参考 这篇文章
当然我们也可以自己通过div来模拟进度条效果,但是缺少语义化。
图片预览
文件上传另一个重要的需求是图片预览
常规的思路是,当前端把图片提交之后,再向服务器中请求图片,但是这恐怕不算是 预览 。
已经上传了,还怎么会叫预览呢?
好在HTML5中已经支持图片预览——FileReader。
对此不熟悉的童鞋可以访问 MDN
FileReader读取的是用户本机上的图片,而不是服务器上的。
因为FileReader读取是异步的,所以这里会有几个钩子
- onabort 读取操作被终止
- onerror 读取操作发生错误
- onload 读取操作成功完成
- onloaded 读取操作完成,但不一定是成功。 可能是 onerror 也可能是 onload
- onprogress 读取操作的进度
function preview(){
var previewArea = $('#previewArea');
var img = docuemnt.createElement('img');
var fileInput = $('#file');
// 这里是假设只上传了一个图片
var file = fileInput.file[0];
previewArea.appendChild(img);
var reader = new FileReader();
// 这个是读取的
reader.readAsDataURL(file);
reader.onload = function(e){
// 这里是把图片资源转化为base64格式了
img.src = e.target.result;
}
}
多文件支持
想让文件上传支持多文件功能, 只需:
<input type='file' multiple>
这样就可以选择多文件上传了,但是需要注意的是,我们之前的代码逻辑都是按照单文件的逻辑来写的
如果是多文件的话,我们就需要把files文件循环添加到formData.append()里面
var fileInput = document.getElementById("myFile");
var files = fileInput.files;
var formData = new FormData();
for(var i = 0; i < files.length; i++) {
var file = files[i];
formData.append('files[]', file, file.name);
}
拖拽
var dropArea;
dropArea = document.getElementById("dropArea");
dropArea.addEventListener("dragenter", handleDragenter, false);
dropArea.addEventListener("dragover", handleDragover, false);
dropArea.addEventListener("drop", handleDrop, false);
// 阻止dragenter和dragover的默认行为,这样才能使drop事件被触发
function handleDragenter(e) {
e.stopPropagation();
e.preventDefault();
}
function handleDragover(e) {
e.stopPropagation();
e.preventDefault();
}
function handleDrop(e) {
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
// handle files ...
}
样式
另一个需求是更改文件上传的默认样式
我们可以通过
<a href='javascript:;' class='file'>选择文件
<input type='file'>
</a>
然后通过设置 a 的样式来达到效果 但是点击的时候还是input的效果 所以我们直接阻止了
a 的默认效果 也可以通过 preventdefault 实现。
代码:
.file {
position: relative;
display: inline-block;
background: #fff;
border: 1px solid #ccc;
border-radius: 4px;
overflow: hidden;
color: #333;
text-decoration: none;
text-indent: 0;
}
.file input {
position: absolute;
font-size: 100px;
right: 0;
top: 0;
opacity: 0;
}
.file:hover {
background: #AADFFD;
border-color: #78C3F3;
color: #004974;
text-decoration: none;
}