在项目中,我们经常会有这样的需求,例如对上传的文件大小进行限制。传统的做法是在服务器端进行限制,例如 Nginx 可以通过 client_max_body_size,PHP 中可以通过 upload_max_filesize 和 post_max_size 设置上传文件的大小。
这种做法的缺陷也很明显:数据已经上传到服务器之后,才进行检测,这会增加带宽和服务器的压力。既然如此,为什么不放到客户端进行检测呢?
在 IE10 及以下版本,可以通过如下方式检测需要上传的文件大小:
<input type="file" onchange="checksize()">
<script>
function checksize(e){
var ev = e || window.event;
var uri = ev.srcElement.value;
var img = new Image();
img.onload = function(){
alert("文件大小为" + (img.fileSize) / 1024 + "KB");
}
img.src = uri;
}
</script>
上面的代码原理是把上传的文件模拟成图片,通过 fileSize 属性获取文件大小。但由于浏览器的安全策略,这种方式几乎只能运行在本地打开网页的情况下。
加上微软在 IE11 中已经废除了 fileSize 属性[1],现代浏览器为了保护本地操作系统,在用户上传文件时,本地资源路径也会使用 fakepath[2],这进一步限制了这种方法获取文件大小的可行性。

在 HTML4 时代,浏览器几乎不提供本地文件操作 API,而 HTML5 给了开发者更大的自由,不仅支持获取上传文件的大小,还支持同时选择多个文件:
<input type="file" multiple="multiple" onchange="checkinfo(this)">
<script>
function checkinfo(obj){
var len = obj.files.length;
var text = "";
for (var i = 0; i < len; i++){
text += "文件:" + obj.files[i].name +
", 大小:" + obj.files[i].size + "字节\n";
}
console.log(text);
}
</script>
在 file 类型的 input 中增加 multiple 属性,即可选择多个文件,而不必通过增加多个 input 来实现。
对于 file 类型的对象,HTML5 增加了一个 FileList 接口,让我们可以访问 <input type="file"> 元素选中的文件列表。它同样可以应用于通过 Drag and Drop API 获取拖拽到 Web 页面上的文件列表。
FileList 提供了 item、length 等属性。更加详细的介绍,可以点击这里。
利用 HTML5,不仅可以获取文件大小,还可以进行本地图片预览:
<input type="file" multiple="multiple" onchange="checkinfo(this)">
<div id="win"></div>
<script>
function checkinfo(obj){
var len = obj.files.length;
for (var i = 0; i < len; i++){
showimg(obj.files[i]);
}
}
function showimg(img){
var a = new FileReader();
a.readAsDataURL(img);
a.onload = function(){
var img = new Image();
img.src = a.result;
document.getElementById('win').appendChild(img);
}
}
</script>
这里用到了 FileReader 这个文件 API,它可以让 Web 应用异步读取本地文件或文件内容,用来操作 File 或 Blob 对象。它的详细介绍可以点击这里。
有了 FileReader 对象,我们就可以完成很多以前无法直接在前端完成的事情,例如 Web 前端图片裁剪:
<input type="file" multiple="multiple" onchange="checkinfo(this)">
<div id="win"></div>
<script>
function checkinfo(obj){
var len = obj.files.length;
for (var i = 0; i < len; i++){
showimg(obj.files[i]);
}
}
function showimg(img){
var a = new FileReader();
a.readAsDataURL(img);
a.onload = function(){
var img = new Image();
img.src = a.result;
document.getElementById('win').appendChild(img);
big2small(a.result);
}
}
function big2small(){
var img = document.getElementsByTagName("img")[0];
var iwidth = img.width;
var iheight = img.height;
var canv = document.createElement('canvas');
var cvx = canv.getContext("2d");
var x = window.prompt("请输入起点X坐标");
var y = window.prompt("请输入起点Y坐标");
var wi = window.prompt("请输入宽度");
var he = window.prompt("请输入高度");
cvx.drawImage(img, x, y, wi, he, 0, 0, wi, he);
document.body.appendChild(canv);
}
</script>
HTML5 中前端文件处理的 File API 还提供了很多其它接口和灵活的应用,例如上传进度提示、文件拖拽、文件读取以及数据分段等。
对于现在的 Web 应用来说,这些 API 让浏览器可以在文件真正上传到服务器之前完成更多处理,也能够减少一些不必要的网络传输。
关于 File API 的介绍,可以点击这里。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。