File API 接口总览:
◆ FileList
接口: 可以用来代表一组文件的JS对象,比如用户通过input[type="file"]
元素选中的本地文件列表
◆ Blob
接口: 用来代表一段二进制数据,并且允许我们通过JS对其数据以字节为单位进行“切割”
◆ File
接口: 用来代表一个文件,是从Blob接口继承而来的,并在此基础上增加了诸如文件名、MIME类型之类的特性
FileUpload 对象
<input type = "file" id = "file" multiple = "multiple" accept = "image/*" />
<input type = "file" id = "file" multiple = "multiple" accept = "image/*" directory webkitdirectory />
FileUpload 对象的属性
accept
设置或返回指示文件传输的MIME
类型的列表(逗号分隔)。accessKey
设置或返回访问 FileUpload 对象的快捷键。multiple
支持多选文件directory(webkitdirectory)
chrome浏览器支持选择文件夹type
返回表单元素的类型。对于 FileUpload ,则是 “file” 。alt
设置或返回不支持<input type="file">
时显示的替代文字。defaultValue
设置或返回 FileUpload 对象的初始值。disabled
设置或返回是否禁用 FileUpload 对象。form
返回对包含 FileUpload 对象的表单的引用。id
设置或返回 FileUpload 对象的 id。name
设置或返回 FileUpload 对象的名称。tabIndex
设置或返回定义 FileUpload 对象的 tab 键控制次序的索引号。value
返回由用户输入设置的文本后,FileUpload 对象的文件名。
accept MIME
*.txt
text/plain*.css
text/css*.csv
text/csv*.htm
text/html*.html
text/html*.js
text/javascript, application/javascript-
*.xml
text/xml, application/xml *.dwg
image/vnd.dwg*.dxf
image/vnd.dxf*.gif
image/gif*.png
image/png*.jp2
image/jp2*.jpe
image/jpeg*.jpeg
image/jpeg*.jpg
image/jpeg*.svf
image/vnd.svf*.tif
image/tiff-
*.tiff
image/tiff *.json
application/json*.pdf
application/pdf*.rtf
application/rtf, text/rtf*.dtd
application/xml-dtd-
*.xhtml
application/xhtml+xml *.doc
application/msword*.dot
application/msword*.asf
application/vnd.ms-asf*.mpp
application/vnd.ms-project*.pot
application/vnd.ms-powerpoint*.pps
application/vnd.ms-powerpoint*.ppt
application/vnd.ms-powerpoint*.wdb
application/vnd.ms-works*.wps
application/vnd.ms-works*.xlc
application/vnd.ms-excel*.xlm
application/vnd.ms-excel*.xls
application/vnd.ms-excel*.xlsx
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet*.xlt
application/vnd.ms-excel*.xlw
application/vnd.ms-excel-
*.ogg
application/ogg, audio/ogg -
*.zip
aplication/zip application/x-zip-compressed *.mpeg
video/mpeg-
*.mpg
video/mpeg *.3gpp
audio/3gpp, video/3gpp*.ac3
audio/ac3*.au
audio/basic*.mp2
audio/mpeg, video/mpeg*.mp3
audio/mpeg*.mp4
audio/mp4, video/mp4
FileUpload 对象的方法
blur()
从 FileUpload 对象上移开焦点。focus()
为 FileUpload 对象赋予焦点。select()
选取 FileUpload 对象。
FileUpload 对象的事件
onchange
:
事件会在域的内容改变时发生。
在file
类型上会出现事件只触发一次问题,每次上传文件的时候,都会将当前的文件路径保存至$event.target.value
中,当第二次选择文件时,由于两次$event.target.value
相同,所以不会触发change事件。解决的方案是:每点击浏览激活onchange
事件一次,就重新初始化一下这个控件
。
JS属性
var files = document.getElementById('file').files;
File:
name
: 文件名称type
: 文件类型size
: 文件大小lastModefiedDate
: 文件的最后修改时间webkitRelativePath
: 选择文件夹模式下,文件相对选择文件夹的相对路径
文件拖拽
拖拽事件:
js能够监听到拖拽的事件有 dragstart
、drag
、 dragend
; dragenter
、 dragover
、 dragleave
、 dragexit
(没有浏览器实现)、 drop
。
其中,与拖拽文件相关的事件有dragenter
(文件拖拽进)、dragover
(文件拖拽在悬浮)、dragleave
(文件拖拽离开)、drop
(文件拖拽放下)。
阻止默认行为:
var dropZone = document.querySelector('#dropZone');
dropZone.addEventListener("dragenter", function (e) {
e.preventDefault();
e.stopPropagation();
}, false);
dropZone.addEventListener("dragover", function (e) {
e.preventDefault();
e.stopPropagation();
}, false);
dropZone.addEventListener("dragleave", function (e) {
e.preventDefault();
e.stopPropagation();
}, false);
dropZone.addEventListener("drop", function (e) {
e.preventDefault();
e.stopPropagation();
// 处理拖拽文件的逻辑
}
注意:
绑定拖拽的容器存在子元素,会造成进入、离开子元素会触发dragenter
和dragleave
事件,不是想要的结果。
<div class="drag-zone">
<!-- 添加一个子元素 -->
<div class="drag-zone--child"></div>
</div>
增加css,保证drag-zone
元素dragging-over
的时候,子元素的pointer-event
都被禁止
.dragging-over *{
pointer-events: none;
}
拖放文件 – 拖放事件:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<title>文件拖拽 (File Drag and Drop)</title>
<style>
#drop_zone {
border: 2px dashed #BBB;
padding: 25px 5px;
text-align: center;
font-size: 20pt;
color: #BBB;
border-radius: 5px;
}
#drop_zone.hovering {
-webkit-box-shadow: inset 0px 0px 50px #BBB;
-moz-box-shadow: inset 0px 0px 50px #BBB;
-o-box-shadow: inset 0px 0px 50px #BBB;
box-shadow: inset 0px 0px 50px #BBB;
}
#output_area {
text-align: left;
}
#output_area li {
margin: 10px 0;
}
</style>
</head>
<body>
<div id="demo_area">
<div class="example">
<div id="drop_zone">将文件拖拽到这里</div>
<div id="output_area"></div>
</div>
</div>
<script>
var output = document.getElementById('output_area');
var dropZone = document.getElementById('drop_zone');
function handleFileDragEnter(e) {
e.stopPropagation();
e.preventDefault();
this.classList.add('hovering');
}
function handleFileDragLeave(e) {
e.stopPropagation();
e.preventDefault();
this.classList.remove('hovering');
}
function handleFileDragOver(e) {
e.stopPropagation();
e.preventDefault();
e.dataTransfer.dropEffect = 'copy';
}
function handleFileDrop(e) {
e.stopPropagation();
e.preventDefault();
this.classList.remove('hovering');
var files = e.dataTransfer.files;
var outputStr = [];
for (var i = 0, f; f = files[i]; i++) {
var lastModified = f.lastModifiedDate;
var lastModifiedStr = lastModified ? lastModified.toLocaleDateString() + ' ' + lastModified.toLocaleTimeString()
: 'n/a';
outputStr += '<li><strong>' + f.name + '</strong> ('
+ (f.type || 'n/a') + ')<br>大小:' + f.size
+ '字节<br>修改时间:' + lastModifiedStr + '</li>';
}
output.innerHTML = '<ul>' + outputStr + '</ul>';
}
dropZone.addEventListener('dragenter', handleFileDragEnter, false);
dropZone.addEventListener('dragleave', handleFileDragLeave, false);
dropZone.addEventListener('dragover', handleFileDragOver, false);
dropZone.addEventListener('drop', handleFileDrop, false);
</script>
</body>
获得拖拽的文件、文件夹
在drop
这个事件的回调中的事件对象能够得到文件对象。
在事件对象中,一个e.dataTransfer
这样的属性,它是一个DataTransfer
类型的数据,有如下的属性:
属性 | 类型 | 说明 |
---|---|---|
dropEffect |
String | 用来hack某些兼容性问题 |
effectAllowed |
String | 暂时不用 |
files |
FileList | 拖拽的文件列表 |
items |
DataTransferItemList | 拖拽的数据(有可能是字符串) |
types |
Array | 拖拽的数据类型 该属性在Safari下比较混乱 |
在Chrome中我们用items
对象获得文件,其他浏览器用files
获得文件.
- 不能用
e.dataTransfer.files
的 File 对象,因为无法通过type
判断一个 File 对象是文件还是文件夹 - 必须用
e.dataTransfer.items
来判断文件或是文件夹。通过kind
属性可以判断当前的DataTransferItem
对象是文件还是字符串:item.kind === "file"
- 使用
webkitGetAsEntry
方法:获取到一个FileSystemFileEntry
对象或FileSystemDirectoryEntry
对象。这两种都继承自FileSystemEntry
- 使用
FileSystemEntry
对象的isFile
属性,判断是文件还是文件夹。 - 文件的话,
entry
的具体类型就是FileSystemFileEntry
,用file
方法获得一个 File 对象:FileSystemFileEntry.file(successCallback[, errorCallback]);
想要保留拖拽的层级结构的话,用fullPath
属性获取 - 文件夹的话,
entry
的具体类型就是FileSystemDirectoryEntry
,可以使用createReader
方法获得一个FileSystemDirectoryReader
对象。reader
的readEntries
方法,获取这个entry
下的子级entries
。
Chrome获取文件 – 禁止上传目录:
dropZone.addEventListener("drop", function (e) {
e.preventDefault();
e.stopPropagation();
var df = e.dataTransfer;
var dropFiles = []; // 存放拖拽的文件对象
if(df.items !== undefined) {
// Chrome有items属性,对Chrome的单独处理
for(var i = 0; i < df.items.length; i++) {
var item = df.items[i];
// 用webkitGetAsEntry禁止上传目录
if(item.kind === "file" && item.webkitGetAsEntry().isFile) {
var file = item.getAsFile();
dropFiles.push(file);
}
}
}
}
Chrome获取文件 – DataTransferItem
:
dropArea.addEventListener("drop", e => {
let items = e.dataTransfer.items;
for (let i = 0; i <= items.length - 1; i++) {
let item = items[i];
if (item.kind === "file") {
// FileSystemFileEntry 或 FileSystemDirectoryEntry 对象
let entry = item.webkitGetAsEntry();
// 递归地获取entry下包含的所有File
this.getFileFromEntryRecursively(entry);
}
}
e.preventDefault();
});
getFileFromEntryRecursively(entry) {
if (entry.isFile) {
entry.file(
file => {
this.addFileToList({ file, path: entry.fullPath });
},
e => { console.log(e); }
);
} else {
let reader = entry.createReader();
reader.readEntries(
entries => {
entries.forEach(entry => this.getFileFromEntryRecursively(entry));
},
e => { console.log(e); }
);
}
}
整合:递归文件、文件夹
- 点击file选择文件
- 点击file选择文件夹
- 拖拽文件、文件夹
获取到 File
对象:
途径 | 得到 File 对象的过程 |
---|---|
拖拽上传 | 在 drop 事件中获取 e.dataTransfer.items ,是一个 DataTransferItemList 对象,遍历得到 DataTransferItem 对象,用 webkitGetAsEntry 方法得到 FileSystemEntry 对象;根据 isFile 属性判断 entry 是文件还是文件夹。是文件的话,用 file 方法获取 File 对象;是文件夹的话,递归地用 reader 读取包含的文件 |
上传文件 | 在 change 事件中获取 input.files ,是一个 FileList 对象,遍历得到 File 对象 |
上传文件夹 | 同上 |
统一格式为 文件夹1/文件夹2/a.txt:
途径 | 相对路径 说明 | |
---|---|---|
拖拽上传 | entry.fullPath.substring(1) |
file.webkitRelativePath 都是空。所以从 entry 中取,注意要把 entry.fullPath 最前面的斜杠去掉 |
上传文件 | file.name |
file.webkitRelativePath 都是空。直接用 file.name |
上传文件夹 | file.webkitRelativePath |
file.webkitRelativePath 就是想要的格式,直接用 |
<style>
span {
border: 1px solid;
}
.dropArea {
border: 10px dashed;
height: 150px;
}
</style>
<span id="uploadFileButton">上传文件</span>
<span id="uploadDirectoryButton">上传文件夹</span>
<section class="dropArea" id="dropArea">把文件拽进来</section>
<script>
new Superloader({
uploadFileEl: document.getElementById("uploadFileButton"),
uploadDirectoryEl: document.getElementById("uploadDirectoryButton"),
dropAreaEl: document.getElementById("dropArea"),
// “三合一”的成功回调,参数 file:文件实体,path:相对路径
successCallback: ({ file, path }) => {
console.log("文件名称:" + file.name + ",相对路径:" + path);
},
});
</script>
class SuperUploader{
constructor({ uploadFileEl, uploadDirectoryEl, dropAreaEl, successCallback }) {
uploadFileEl && this.initUploadFile(uploadFileEl, successCallback);
uploadDirectoryEl && this.initUploadDirectory(uploadDirectoryEl, successCallback);
dropAreaEl && this.initDragAndDrop(dropAreaEl, successCallback);
}
initUploadFile(el, successCallback) {
const oRealButton = document.createElement("input");
oRealButton.setAttribute("type", "file");
oRealButton.setAttribute("multiple", true);
oRealButton.style.display = "none";
oRealButton.addEventListener("change", e => {
let files = oRealButton.files;
for (let i = 0; i <= files.length - 1; i++) {
let file = files[i];
// 这里的 file.webkitRelativePath 都是 "" ,不是我们想要的.要用 file.name
successCallback({ file, path: file.name });
}
})
document.body.appendChild(oRealButton);
el.addEventListener("click", e => {
oRealButton.click();
});
}
initUploadDirectory(el, successCallback) {
const oRealButton = document.createElement("input");
oRealButton.setAttribute("type", "file");
oRealButton.setAttribute("webkitdirectory", true);
oRealButton.style.display = "none";
oRealButton.addEventListener("change", e => {
let files = oRealButton.files;
for (let i = 0; i <= files.length - 1; i++) {
let file = files[i];
// 这里的 file.webkitRelativePath 就是我们想要的格式
successCallback({ file, path: file.webkitRelativePath });
}
})
document.body.appendChild(oRealButton);
el.addEventListener("click", e => {
oRealButton.click();
});
}
initDragAndDrop(el, successCallback) {
el.addEventListener("dragover", e => {
e.preventDefault();
});
el.addEventListener("drop", e => {
let items = e.dataTransfer.items;
for (let i = 0; i <= items.length - 1; i++) {
let item = items[i];
if (item.kind === "file") {
let entry = item.webkitGetAsEntry();
this.getFileFromEntryRecursively(entry, successCallback);
}
}
e.preventDefault();
});
}
getFileFromEntryRecursively(entry, successCallback) {
if (entry.isFile) {
entry.file(file => {
// 这里的 file.webkitRelativePath 都是 "" ,不是我们想要的.
// entry.fullPath 是前面带斜杠的,要把斜杠去掉的
let path = entry.fullPath.substring(1);
successCallback({ file, path });
}, e => { console.log(e); });
} else {
let reader = entry.createReader();
reader.readEntries(entries => {
entries.forEach(entry => this.getFileFromEntryRecursively(entry, successCallback));
}, e => { console.log(e); });
}
}
}
文件上传
一、读取文件为blob并上传到服务器
HTML
<div class="container">
<!--读取要上传的文件-->
<input type="file" id="file" />
<input type="button" id="btn1" value="点击上传" onclick="uploadClick();" />
</div>
JS
//读取图片实例,并上传到服务器
var fileBox = document.getElementById('file');
fileBox.onchange = function () {
//获取选择文件的数组
var fileList = fileBox.files;
for (var i = 0; i < fileList.length; i++) {
var file = fileList[i];
uploadFile(file);
}
}
//关键代码上传到服务器
function uploadFile(file) {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function () {
var blob = new Blob([reader.result]);
//提交到服务器
var fd = new FormData();
fd.append('file', blob);
var ext = file.name.substring(file.name.lastIndexOf('.'));
fd.append('extention', ext);
fd.append('maxsize', 1024*1024*4);//Asp.net 默认一次上传最大4MB
fd.append('isClip', -1);
var xhr = new XMLHttpRequest();
xhr.open('post', '../ashx/upload.ashx', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var data = eval('(' + xhr.responseText + ')');
console.info(data);
if (data.success) {
//上传成功
var imgName = data.msg;
alert(imgName);
} else {
alert(data.msg);
}
}
}
//开始发送
xhr.send(fd);
}
}
后台C#处理关键代码:
//接收文件
HttpRequest req = _Context.Request;
string newname = _fileInfo.CreateNewName(newExtention);
//接收二级制数据并保存
Stream stream = _PostedFile.InputStream;
byte[] dataOne = new byte[stream.Length];
stream.Read(dataOne, 0, dataOne.Length);
FileStream fs = new FileStream(_fileInfo.TempFile + newname, FileMode.Create, FileAccess.Write);
try
{
fs.Write(dataOne, 0, dataOne.Length);
}
finally
{
fs.Close();
stream.Close();
}
return newname;
二、读取图片文件,并上传到服务器
HTML
<div class="container">
<!--图片类型验证方法1-->
<input type="file" id="file" accept="image/*" />
<input type="button" id="btn1" value="点击上传" onclick="uploadClick();" />
<h4>选择文件如下:</h4>
<img src="" id="img1" />
</div>
JS:
//读取图片内容 为DataURL
function readFile(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function (e) {
var result = reader.result;
$('.container blockquote').text(result);
$('#img1').attr('src', result)
}
}
//读取图片实例,并上传到服务器
var fileBox = document.getElementById('file');
fileBox.onchange = function () {
//获取选择文件的数组
var fileList = fileBox.files;
for (var i = 0; i < fileList.length; i++) {
var file = fileList[i];
//图片类型验证第二种方式
if (/image\/\w+/.test(file.type))
readFile(file);
else
console.log(file.name + ':不是图片');
}
}
//关键代码上传到服务器
function uploadFile(file) {
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function () {
var blob = new Blob([reader.result], { type: 'image/jpg' });
//提交到服务器
var fd = new FormData();
fd.append('file', blob);
var ext = file.name.substring(file.name.lastIndexOf('.'));
fd.append('extention', ext);
fd.append('isClip', -1);
var xhr = new XMLHttpRequest();
xhr.open('post', '../ashx/upload.ashx', true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4 && xhr.status == 200) {
var data = eval('(' + xhr.responseText + ')');
console.info(data);
if (data.success) {
//上传成功
var imgName = data.msg;
alert(imgName);
} else {
alert(data.msg);
}
}
}
//开始发送
xhr.send(fd);
}
}
//读取二进制图片文件,并上传到服务器
function uploadClick() {
var fileList = fileBox.files;
for (var i = 0; i < fileList.length; i++) {
var file = fileList[i];
//图片类型验证第二种方式
if (/image\/\w+/.test(file.type))
uploadFile(file);
else
console.log(file.name + ':不是图片');
}
}