在前端开发中,拖拽上传文件是一种很常见的需求。通过拖拽来实现文件上传需要了解FileReader这个File API以及对应的拖拽事件
我们先了解一下,在前端表单中是如何处理文件上传的
files集合
一直以来,我们都通过<input type='file'>字段来获取文件的对应信息。HTML5为文件输入元素添加了一个files集合,在files集合中,有一组File对象,每个File对象对应着一个文件
File对象属性(只读属性):
| 属性 | 类型 | 描述 |
|---|---|---|
| name | 字符串 | 本地文件系统中的文件名 |
| size | 数字 | 文件的字节大小 |
| type | 字符串 | 文件的MIME类型 |
| lastModifiedDate | 字符串 | 文件上一次被修改的时间 |
具体的实现代码如下:
<input type='file' id='files'>
<script>
var files = document.getElementById('files')
files.addEventListener('change', function(event) {
console.log(event.target.files)
console.log(
`文件名称:${event.target.files[0].name}
文件类型:${event.target.files[0].type}
文件大小:${event.target.files[0].size} bytes`
)
})
</script>
实例结果截图:

靠files集合只能知道一些文件的基本信息,FileReader类型才是File API的重头戏
FileReader类型
FileReader类型实现是一种异步文件读取机制,读取的是文件系统。可以通过以下方法读取文件中的内容
| 方法名 | 参数 | 输出内容 | 描述 |
|---|---|---|---|
| readAsText | file,encoding | 纯文本 | 异步以纯文本形式读取文件,将读取到的文本保存在result属性中 |
| readAsDataURL | file | DataURI的形式 | 异步读取文件并将文本以数据URI的形式保存在result属性中 |
| readAsBinaryString | file | 二进制 | 异步读取文件内容并将包含文件内容的二进制字符串保存在result属性中 |
| readAsArrayBuffer | file | ArrayBuffer | 异步读取文件并将一个包含文件内容的ArrayBuffer保存在result属性中 |
| abort | -- | -- | 用于中断文件读取过程 |
具体文件输出代码:
<input type='file' id='files'>
<script>
var files = document.getElementById('files')
files.addEventListener('change', function (event) {
let file = event.target.files[0]
let reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = function () {
console.log(reader.result)
}
})
</script>
我们以上传图片,输出DataURL为例

其他三种不同的文件输出,只需将上述reader.readAsDataURL(file)的代码替换方法即可,由于篇幅有限,就不在此展开,读者可以创建对应的文件一一尝试
因为文件读取过程是异步的,FileReader也提供了几个对应的事件,同时在上述的代码中,我们也已经使用了FileReader中的load事件来判断是否已经读完整个文件
| 事件名称 | 事件描述 |
|---|---|
| onabort | 读取被终止时调用 |
| onerror | 读取出错时调用 |
| onload | 读取成功时调用 |
| onloadend | 读取完成时调用,不管是读取成功还是失败 |
| onloadstart | 读取将要开始时出发 |
| onprogress | 读取过程中周期性触发,每50ms左右触发一次,可以用来监听文件读取的进度 |
读取部分内容
有时候,我们只想读取文件的一部分而不是全部内容。为此,File对象还支持了一个slice()方法,slice()方法接受两个参数:起始字节数以及要读取的字节数。这个方法返回一个Blob实例,Blob是File类型的父类型
我们新建一个从1到10的txt,读取1-5的内容

输出结果:

以下就是读取文件部分内容的代码
<input type='file' id='files'>
<script>
var files = document.getElementById('files')
files.addEventListener('change', function (event) {
let file = event.target.files[0]
let reader = new FileReader()
blob = file.slice(0, 10)
reader.readAsText(blob)
reader.onload = function () {
console.log(reader.result)
}
})
</script>
对象URL
有时候我们想要上传图片之后,直接在网页中展示出来,这时候,我们就可以使用对象URL。
对象URL也被称为blob URL,指的是引用保存在File或Blob中数据的URL,要创建对象URL,可以使用window.URL.createObjcetURL()方法,并传入File或Blob对象,方法就会返回一个字符串,指向一块内存地址。那么我们就可以动态将字符串URL插入img标签中,从而实现网页上传图片展示了

具体代码如下:
<input type='file' id='files'>
<div id='images'></div>
<script>
var files = document.getElementById('files')
var images = document.getElementById('images')
files.addEventListener('change', function (event) {
let file = event.target.files[0]
url = window.URL.createObjectURL(file)
if (url) {
images.innerHTML = `<img width='300' height='300' src=${url} />`
}
})
</script>
如果已经不需要了相对应的对象URL数据,最好释放它占用的内容。我们可以通过将对象URL传给window.URL.revokeObjectURL(),那么就能手工释放其占用的内存
读取拖拽文件
我们可以结合HTML5拖拽API和文件API,来实现从桌面把文件拖放到对应网页的目标上。从桌面上把文件拖放到浏览器中会触发drop事件,从而就能通过event.dataTransfer.files读取到拖拽文件的信息
当文件拖拽网页位置上之后能获取到文件的信息:

具体代码如下:
<div id='droptarget' style="width: 200px; height: 200px; border:1px solid black"></div>
<script>
var droptarget = document.getElementById('droptarget')
droptarget.addEventListener('drop', function (event) {
event.preventDefault(); //禁止浏览器默认行为,浏览器默认会直接打开显示
if (event.type === 'drop') {
//获取文件信息,同File对象相同
var files = event.dataTransfer.files;
console.log(
`文件名称:${files[0].name},文件类型:${files[0].type},文件大小:${files[0].size} bytes`
)
}
})
droptarget.addEventListener('dragenter', function (event) {
//禁止浏览器默认行为,浏览器默认会直接打开显示
event.preventDefault();
})
droptarget.addEventListener('dragover', function (event) {
//禁止浏览器默认行为,浏览器默认会直接打开显示
event.preventDefault();
})
</script>
使用XHR文件上传
我们已经可以通过File API来访问到文件中的内容,那么我们就可以利用这一点来通过XHR实现文件的上传。我们可以通过FormData以表单的方式来上传文件内容。首先我们创建一个FormData对象,通过调用append()方法并传入相应的File对象作为参数,再通过XHR的send()方法,就可以将文件内容上传到服务器端。
具体的代码根据上面的读取拖拽文件代码进行改造,从而实现拖拽文件上传:
droptarget.addEventListener('drop', function (event) {
event.preventDefault();
if (event.type === 'drop') {
var data = new FormData();
var files = event.dataTransfer.files;
data.append("file", files[0]) //如果有多个文件上传,可对应遍历files对象加入到data中
var xhr = new XMLHttpRequest();
xhr.open('post', url, true); //url表示文件上传服务器的地址
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
console.log(xhr.responseText)
}
}
}
xhr.send(data);
})
最后
如果觉得文章对你有帮助,请帮忙多多点赞,动动小手,你的点赞是我写作最大的动力