前言
今天我们聊一聊图片上传,单张Or多张 ,如今,各大图片上传插件数不胜数,例如:Jquery的 verupload.js,jQuery File Upload、Uploadify、jQuery.filter等等。But。上面说到的这些插件,今天我们不谈,我们来看一看使用HTML5中的FileReader 如何实现 图片的单张及多张预览、删除、上传等功能。
先看下实现后的效果如下:
实现
前端部分
这块是用户点击按钮 其中我们最重要的一句话是input type=file 和给了一个multiple属性,可以满足多张图片上传
<div class=\"form-group form-row\"> <label class=\"col-sm-2 control-label uText\">俱乐部相册</label><div class=\"row\"> <div class=\"col-xs-10 col-sm-8 mTop5\"> <label title=\"上传俱乐部相册\" for=\"ClubImagesUpload\" id=\"btnClubImg\" class=\"col-sm-2\"> <input type=\"file\" accept=\"image/*\" name=\"ClubImagesUpload\" id=\"ClubImagesUpload\" class=\"hide\" multiple=\"multiple\"> <img src=\"/Images/registerNewSite/btn_addimg.png\" class=\"addImg\"/> </label> </div> </div></div>
下面这块区域是用于图片预览的
<div class=\"form-group form-row\" id=\"preViewMore\"><div class=\"row\"> <div class=\"col-xs-9\"> <div id=\"clubInputImagePreview\" class=\"col-sm-9 img-preview img-preview-sm\"></div> </div> </div></div>
样式部分
什么?连样式你都要看,还有没有人性(苦笑脸)
Js部分
首先我们分析下上面的html,我们用一个label把input和一个img标签包起来了,我们希望点击效果图那个+号图片,就能弹出选择相片的对话框,所以,我们需要先给label来一个点击事件:
$(\"#btnClubImg\").click(function() { //TODO Something });
接着我们再看,因为我们是要获取上传的文件,而我们的文件主要是在input上,所以,我们先将input标签获取到:
$(\"#btnClubImg\").click(function() { var $input = $(\"#ClubImagesUpload\"); console.log($input);//打印当前元素 });
我们将当前input元素标签打印出来看看是个什么东东
我们展开第一项会发现files里面length长度是0
好,我们继续分析,因为我们想要能当input框改变的时候,说简单点 就是有选择到文件的时候,我们能获取到当前选择的文件,这个和获取input框文字输入是一样的道理,所以,经过分析,我们知道需要给input标签加一个change事件:
$(\"#btnClubImg\").click(function() { var $input = $(\"#ClubImagesUpload\"); console.log($input); $input.on(\"change\", function () { console.log(this);//打印改变后的当前元素 }); });
让我们来瞄一眼,获取到改变后的input元素里面有些啥东东:
这里很清楚得可以看到,我们获取到了选择的图片,包括有最后修改事件,图片名称,大小以及图片类型(有了文件类型,我们就可以判断当前用户选择的是否是图片不是吗(斜眼笑))
同样,这是单个文件的, 如果是多个文件,就会有多个file
接着往下看,通过打印输出我们可以看到,我们再input 标签的files元素上已经拿到了我们想要的文件信息,我们只需要获取它们就行了:
var files = this.files; var length = files.length;
这样,我们就可以获取到所有文件,以及文件的个数,那这里问题来了,我们如果是选择多个文件,如果将其依次输出并展示到页面上呢?
看到上面标注的四个字,脑袋中有没有闪现出两个字呢?循环
$.each(files, function (key, value) { //TOTO Something });
通过将上面得到的files 循环,我们可以依次得到每个文件的信息。这样,你就不仅可以将其依次输出,如果你愿意,你还可以将其送上天~
var fileReader = new FileReader();//实例化一个FileReader对象 var file_ = files[key];//获取当前文件 if (/^image\\/\\w+$/.test(file_.type)) {//将当前文件进行正则匹配,看是否是选择的图片 fileReader.onload = function() {//当读取操作完成时调用 } }
有必要延伸下FileReader的知识点:
FileReader主要用于将文件内容读入内存,通过一系列异步接口,可以在主线程中访问本地文件。
使用FileReader对象,web应用程序可以异步的读取存储在用户计算机上的文件(或者原始数据缓冲)内容,可以使用File对象或者Blob对象来指定所要处理的文件或数据。
回到主题,我们已经能够得到文件并且得到返回,所以此时,我们只需要展示返回的结果就行了
$(\"#clubInputImagePreview\").css(\"background-image\", \"url(\" + this.result + \")\");
我们将其this.result打印出来看看是个什么东东:
不言而喻,是将图片转换成了Base64的数据格式。最后,我们调用 readAsDataURL 读取文件内容,将其用data:url字符串表示出来
fileReader.readAsDataURL(value);
这样,你就可以得到一个简易的图片上传的Demo了,但是并不是最终的,因为你还需要加很多业务进去。比如:得到一张预览图片后,当前标签会被占用,如果下次循环进来,直接使用原标签,肯定会将之前的图片替换,那这肯定不是我们想要的效果,
我们希望是能依次展示,而不是替换展示。所以,我们还需要做一些处理:
$(\"#clubInputImagePreview\").css(\"background-image\", \"url(\" + this.result + \")\"); //使用apend再当前元素下追加一个子节点 $(\"#clubInputImagePreview\") .append(\"<img src=\'/Images/registerNewSite/btn_r_del.png\' class=\'clubsImage\' id=\'ImgRemove\' />\"); //使用after 向当前兄弟节点 追加一个同级节点 $(\"#clubInputImagePreview\").after( \"<div id=\'clubInputImagePreview1\' class=\'col-sm-9 img-preview img-preview-sm delImg\' ></div>\");
然后我们追加的删除图片,也需要给其点击事件,让我们的当前预览区域消失:
$(\"#ImgRemove\").click(function () { $(this).parent().remove(); });
最后,你会发现结果还不是我们想要的,那是因为 当前ID还在,所以无法进行下一步操作,而我们只需要将当前元素的Id Remove掉,然后新增一个同ID的元素,这样浏览器就会认为这是一个新的元素:
$input.removeAttr(\"id\"); var newInput =\'<input type=\"file\" accept=\"image/*\" name=\"ClubImagesUpload\" id=\"ClubImagesUpload\" class=\"hide\" multiple=\"multiple\">\'; $(this).append($(newInput));
最后完整JS代码如下:
var intP = 0; $(\"#btnClubImg\").click(function() { var $input = $(\"#ClubImagesUpload\"); // console.log($input); $input.on(\"change\", function () { // console.log(this); var files = this.files; var length = files.length; if (intP > 8) { layer.msg(\'图片不能再多了~\', {}); return; } $.each(files, function (key, value) { var fileReader = new FileReader(); var file_ = files[key]; if (/^image\\/\\w+$/.test(file_.type)) { fileReader.onload = function() { if (intP > 8) { layer.msg(\'图片不能再多了~\', {}); return; } if (key == 0 && intP == 0) { console.log(this.result); $(\"#clubInputImagePreview\").css(\"background-image\", \"url(\" + this.result + \")\"); $(\"#clubInputImagePreview\") .append( \"<img src=\'/Images/registerNewSite/btn_r_del.png\' class=\'clubsImage\' id=\'ImgRemove\' />\"); $(\"#clubInputImagePreview\").after( \"<div id=\'clubInputImagePreview1\' class=\'col-sm-9 img-preview img-preview-sm delImg\'></div>\"); } else { $(\"#clubInputImagePreview\" + parseInt(intP) + \"\").css(\"background-image\", \"url(\" + this.result + \")\"); $(\"#clubInputImagePreview\" + parseInt(intP) + \"\").append( \"<img src=\'/Images/registerNewSite/btn_r_del.png\' class=\'clubsImage\' id=\'ImgRemove\" + parseInt(parseInt(1) + parseInt(intP)) +\"\' />\"); $(\"#clubInputImagePreview\" + parseInt(intP) + \"\").after( \"<div id=\'clubInputImagePreview\" + parseInt(parseInt(1) + parseInt(intP)) + \"\'class=\'col-sm-9 img-preview img-preview-sm delImg\' ></div>\"); } if (key == 0 && intP == 0) { $(\"#ImgRemove\").click(function () { $(this).parent().remove(); }); } else { $(\"#ImgRemove\" + parseInt(parseInt(1) + parseInt(intP)) + \"\").click(function () { $(this).parent().remove(); }); } intP += parseInt(1); }; fileReader.readAsDataURL(value); } else { layer.msg(\"格式错误<br/>请选择一个图片文件\"); } }); }); $input.removeAttr(\"id\"); var newInput = \'<input type=\"file\" accept=\"image/*\" name=\"ClubImagesUpload\" id=\"ClubImagesUpload\" class=\"hide\" multiple=\"multiple\">\'; $(this).append($(newInput)); });
下一篇,将讲述 如何将前端得到的多个File ,存入进数据库,后端是.NET Core,敬请期待吧~