文件上传

简单的文件上传

1
2
3
4
<form id="uploadForm" method="POST" action="upload" enctype="multipart/form-data">
<input type="file" id="myFile" name="file"></input>
<input type="submit" value="提交"></input>
</form>

这是一个最简单的文件上传。但是。。。ummmm,够胆你就提交这样的代码吧

常用的上传方式——FormData

通过在脚本里新建FormData对象,把file对象设置到表单项中,然后利用XMLHttpRequest异步上传到服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var xhr = new XMLHttpRequest()
var formData = new FormData()
var fileInput = document.getElementById('myFil
e')
var file = fileInput.files[0]
formData.append('filename', file)

console.log(formData) // 打印不出来东西
// 直接打印是看不到的,要用FormData的get()和getAll()方法
formData.getAll("filename")

xhr.open("post", '/upload.js')

xhr.onload = function () {
if (this.status === 200) {
// 对请求成功的处理
console.log("请求成功")
}
}

xhr.send(formData)
xhr = null

以上,可以完成最基本的需求

通过jQuery Ajax使用FormData对象上传文件

上传进度

XMLHttpRequest Level 2中,传送数据的时候,有一个progress事件,上传数据progress事件属于XMLHttpRequest.upload对象,上传数据过程中会触发,这个对象拥有下列方法:

这个对象拥有下列下列方法:

  • onloadstart
  • onprogress
  • onabort
  • onerror
  • onload
  • ontimeout
  • onloadend

这些方法在XHR对象中都存在同名版本,区别是后者是用于加载资源时,而前者用于资源上传时。其中onprogress 事件回调方法可用于跟踪资源上传的进度,它的event参数对象包含两个重要的属性loaded和total。分别代表当前已上传的字节数(number of bytes)和文件的总字节数。比如我们可以这样计算进度百分比:

1
2
3
4
5
6
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) { // lengthComputable ---- 文件总大小是否可知
var percent = (e.loaded / e.total) * 100
// 对进度进行处理
}
}

现代浏览器配合h5提供的progress享用

1
2
<progress id="myProgress" value="50" max="100">
</progress>

其value属性绑定上面代码中的percent的值即可。

因为xhr.upload.onprogress在上传阶段(即xhr.send()之后,xhr.readystate=2之前)触发,每50ms触发一次。所以文件太小网络环境好的时候是直接到100%的。

图片预览

普通青年的图片预览方式是待文件上传成功后,后台返回上传文件的url,然后把预览图片的img元素的src指向该url。这其实达不到预览的效果和目的。

属于文艺青年的现代浏览器又登场了:“使用HTML5的FileReader API吧!” 让我们直接上代码,直奔主题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function handleImageFile(file) {
var previewArea = document.getElementById('previewArea');
var img = document.createElement('img');
var fileInput = document.getElementById("myFile");
var file = fileInput.files[0];
img.file = file;
previewArea.appendChild(img);

var reader = new FileReader();
reader.onload = (function(aImg) {
return function(e) {
aImg.src = e.target.result;
}
})(img);
reader.readAsDataURL(file);
}

这里我们使用FileReader来处理图片的异步加载。在创建新的FileReader对象之后,我们建立了onload函数,然后调用readAsDataURL()开始在后台进行读取操作。当图像文件加载后,转换成一个 data: URL,并传递到onload回调函数中设置给img的src。

另外我们还可以通过使用对象URL来实现预览

1
2
3
4
5
6
7
var img = document.createElement("img");
img.src = window.URL.createObjectURL(file);;
img.onload = function() {
// 明确地通过调用释放
window.URL.revokeObjectURL(this.src);
}
previewArea.appendChild(img);

多文件上传

1
<input type="file" id="myFile" multiple>

此时代码里拿到的FileUpload对象的files属性就是一个选中的多文件的数组了

1
2
3
4
5
6
7
8
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);
}

FormData的append方法提供第三个可选参数用于指定文件名,这样就可以使用同一个表单项名,然后用文件名区分上传的多个文件。这样也方便前后台的循环操作。

拖拽选择文件

利用HTML5的drag & drop事件来实现对拖拽的支持。首先我们可能需要确定一个允许拖放的区域,然后绑定相应的事件进行处理。

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
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 ...
}

这里可以把通过事件对象的dataTransfer拿到的files数组和之前相同处理,以实现预览上传等功能。有了这些事件回调,我们也可以在不同的事件给我们UI元素添加不同的class来实现更好交互效果。

end

此外上传还有二进制上传,借用iframe实现上传,详情请参考这篇文章

参考

文件上传那些事儿

前端文件上传基础

FormData

html拖放API