前端本地文件上传预览

众所周知,前端无法像原生APP一样直接操作本地文件 —— 否则打开一个网页操控JS就能把用户电脑上的文件偷光。所以需要通过用户触发:通常,用户可选择以下两种方式触发

  1. 通过input type="file" 选择本地文件
  2. 通过拖拽方式把文件“拉到”指定地方

第一种是最常用的手段,通常还会自定义一个按钮,然后盖在他上面:因为type="file" 的input不容易改变样式。
如下笔者写一个选择控件,并将其包裹在form里:

<form>
	<input type="file" id="file-input" data-t="file" name="fileContent" />
</form>

然后就可以用FormData获取整个表单的内容了

//jQuery
$("#file-input").on("change",function(){
	console.log(`file name is ${this.value}`);
	let formData=new FormData(this.form);
	formData.append("fileName",this.value);
	console.log(formData);
})

input中增加data-t属性是为了防止有些浏览器默认会将不认识的type值变为text

其中,获取的this.value 是本地文件路径,也就是说在浏览器无法获取到文件的真实存放位置。同时FormData打印出来是一个空的Object —— 但不是说它的内容是空的,只是它对前端开发者来说是“透明”(不可见)的,只能append添加字段。

FormData无法得到文件的内容,而使用FileReader可以读取整个文件的相关信息:用户选择文件后,通过input.files 就可以得到选中的文件。我们以图片为例说明:

$("#file-input").on("change",function(){
	let fileReader=new FileReader(),
		fileType=this.files[0].type;
	fileReader.onload=function(){
		if(/^image\/[jpg|png|gif]/.test(fileType)){
			$(`<img src="${this.result}" />`).appendTo("body")
		}
	}
	console.log(this.files[0]);
	//base64方式读取:图片等文件通用读取方式
	fileReader.readAsDataURL(this.files[0]);
})

运行之后,你会如愿发现在按钮下面多出来了一张图片!

但是,上面这段代码的运行顺序是什么呢?是和往常一样“顺序”执行吗?
你如果在onload里面加上console的话就会发现onload的代码是最后执行的!

onload加载,如果遇到图片、表格等,则会等到这些都加载完成后才执行里面的代码。
(这也是前端性能优化中建议在一些场景下用window.addEventListener('DOMContentLoaded',function(){}) 代替window.onload 的原因)

前端本地文件上传预览

把原始的File对象(代码中this.files[0])打印出来是这样的:
前端本地文件上传预览

它是一个window.File的实例,包含了文件修改时间、文件名、文件大小、文件的mime类型等。如果需要限制上传文件大小就可以通过判断size属性是否超出范围,单位是字节,而要判断是否为图片文件就可以通过type类型是否以image开头 —— 通过判断文件名的后缀可能会有不准。还有!目前只有jpg/gif/png三种格式的图片可以用img展示出来。

代码中笔者实例化了一个FileReader,调用它的readAsDataURL并把File对象传给他,监听它的onload事件,load完读取的结果就在他的result属性里了。它是一个base64格式的,所以可以直接赋值给img的src

其实FileReader除了可以读取为base64外,还可以读取以下格式:
以原始二进制方式读取,读取结果可直接转成整数数组:fileReader.readAsArrayBuffer(this.files[0]);

“巧”的是,ArrayBuffer内容也同样不可见。但是可以通过ArrayBuffer.length得到长度,还能转成整形数组,从而知道文件的原始二进制内容:

let buffer=this.result;
//依次每字节8位读取,放到一个整数数组
let view=new Uint8Array(buffer);
console.log(view)

当然,我们再来说说这第二种方式:拖拽。

<div class="img-container">
	drop your image here
</div>

也就一个框,里面一行字,让我们将css忽略。
去监听它的拖拽事件:

$(".img-container").on("dragover",function(event){
	event.preventDefault();
})
.on("drop",function(event){
	event.preventDefault();
	let fileReader=new FileReader(),
		file=event.originalEvent.dataTransfer.files[0];
	let fileType=file.type;
	fileReader.onload=function(){
		if(/^image\/[jpg|png|gif]/.test(fileType)){
			$(`<img src="${this.result}" />`).appendTo('.img-container');
		}
	}
	fileReader.readAsDataURL(file);
});

代码中运用了es6的链式调用,不过这不重要。由上面可以知道:这种方式数据在event的dataTransfer对象里。拿到这个对象以后,就可以和第一种方式:输入框一样了,即使用FileReader读取。
或者新建一个空的formData,然后把它append到formData里:

let formData=new FormData();
formData.append("fileContent",file);
上一篇:js 获取表单数据


下一篇:FormData