小言_互联网的博客

前端本地文件上传预览

389人阅读  评论(0)

众所周知,前端无法像原生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);

2020-09-15更新

发现了一个新写法:似乎可以借助“input框本质上还是一个写入区域”,在有文件选择时将文件路径转为URL写入img;没有时直接将内部value数据写入:

<input type="file" id='id-face' name='face'  accept="image/*" />
<div id='face-empty-result'>
    <img style='width:4rem' src="https://github.com/wangheng3751/my-resources/blob/master/images/camera.png?raw=true" alt="">
    <p>身份证正面照</p>
</div>

document.getElementById("id-face").addEventListener("change", function(){
   
    onFileChange(this,"face-result","face-empty-result")
});

function onFileChange(fileObj,el,btnel){
   
    console.log(fileObj);   //上图
    var windowURL = window.URL || window.webkitURL;
    var dataURL;
    var imgObj = document.getElementById(el);
    document.getElementById(btnel).style.display="none";
    imgObj.style.display="block";
    if (fileObj && fileObj.files && fileObj.files[0]) {
   
        dataURL = windowURL.createObjectURL(fileObj.files[0]);
        imgObj.src=dataURL;
    } else {
   
        dataURL = fileObj.value;
        imgObj.style.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)";
        imgObj.filter.item("DXImageTransform.Microsoft.AlphaImageLoader").src = dataURL;
    }
}

转载:https://blog.csdn.net/qq_43624878/article/details/106782034
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场