javascript实现图片上传以及预览(包含相关知识点汇总)

来源:互联网 发布:阿里云空间购买多少钱 编辑:程序博客网 时间:2024/06/05 06:28

前言

前端工作过程中有这么一个基本需求,要求将图片从本地选择上传到服务器,同时在本地进行预览。
这对于我这只小菜鸟来说也是难啊,为了预防写完了就忘,以及知识太过杂乱而不系统,在这里重新整理一下。

正文

预览代码

二话不说,先把成功预览图片的代码贴上来。
在这里我用的是vue,所以图片路径是经过vue数据绑定的。

// .html// input file输入框可以设置opacity为0,然后绝对定位覆盖<img :src="head_img" alt="头像"><input type="file" @change="onFileChange" id="file">// .jsdata() {    return {        header_img: ''    }},// 图片上传主要是靠FileReader对象methods: {    onFileChange() {        if (typeof FileReader === 'undefined') {            console.log('false');            return false;        }        var file = document.getElementById('file').files[0];        var reader = new FileReader();        reader.readAsDataURL(file);        var vm = this;        reader.onload = function(e) {            vm.image = e.target.result;        }    }}

初学者看到上述代码可能会有些不了解的地方,这样就让我们带着问题阅读下述知识点~

在Web应用中使用文件

在学习使用FileReader对象时,首先需要先了解在Web应用中应该如何使用文件。

使用在HTML5中添加到DOM的File API,现在可以让Web内容要求用户选择本地文件,然后读取这些文件的内容。
此选择可以通过使用HTML<input>标签或者拖放来实现。

1.访问所选的文件

请看下面的HTML:

<input type="file" id="input"><input type="file" id="input" multiple >

通过File API,我们可以在用户选取一个或者多个文件之后,访问到代表了所选文件的一个或多个File对象,这些对象被全部包含在一个FileList对象中。

PS:
所以这里document.getElementById('file').files得到的是一个FileList对象。
如果你得到一个“files is undefined”错误,那么就是你没有选择正确的HTML元素

2.在一个change事件发生时,访问选择的文件

另外,还可以在input元素上的change事件触发时再访问它的FileList属性(但并非强制性):

<input type="file" id="input" onchange="handleFiles(this.files)">

当用户成功选取若干个文件后,handleFiles()函数会被调用,且一个代表用户所选择的文件包含了File对象的FileList对象会作为参数传入该函数。

如果想选择多个文件,记得要在input元素上加上multiple属性:

<input type="file" id="input" multiple onchange="handleFiles(this.files)">

在用户选择了多个文件的情况下,传入handleFiles()函数的文件列表将会包含多个File对象,每个File对象对应一个真实的文件。

PS:当然也可以通过动态添加change事件监听器

var inputElement = document.getElementById("inputField");inputElement.addEventListener("change", handleFiles, false);function handleFiles() {  var fileList = this.files; }

3.获取所选文件的信息

用户将所选择的文件都存储在了一个FileList对象上,其中每个文件都对应了一个File对象,你可以通过这个FileList对象的length属性知道用户一共选择了多少个文件:

var numFiles = files.length;

可以通过普通的循环语句来操作每个单独的File对象:

for (var i = 0, numFiles = files.length; i < numFiles; i++) {  var file = files[i];  ..}

file对象有3个属性:
①name:文件名,只读字符串;
②size:文件大小,单位为字节
③type:MIME类型,只读字符串

4.在隐藏的文件输入框上调用click()方法

在上述写的预览代码中,隐藏<input>的方法是设置opacity为0,绝对定位覆盖,但其实官方已有其他方法。

display:none隐藏,在需要的时候调用处它的click()方法,实现自定义界面打开文件选择对话框。

参考下列代码:

// html// input的accept="image/*"表示可以接受任何格式的图片<input type="file" id="fileElem" multiple accept="image/*" style="display:none" onchange="handleFiles(this.files)"><a href="#" id="fileSelect">Select some files</a>// jsvar fileSelect = document.getElementById("fileSelect"),    fileElem = document.getElementById("fileElem");fileSelect.addEventListener("click", function (e) {  if (fileElem) {    fileElem.click();  }  e.preventDefault(); // prevent navigation to "#"}, false);

这样可以通过改变按钮的样式来自定义了。

5.通过拖放操作选择文件

你可以让用户将本地文件拖放到你的应用程序上。

首先要创建一个拖放操作的目的区域。可根据应用程序的设计来决定哪部分的内容接受drop,但创建一个接收drop事件的元素是简单的:

var dropbox;// dropbox是我们拖放目的区域// 绑定dragenter、dragover以及drop事件dropbox = document.getElementById("dropbox");dropbox.addEventListener("dragenter", dragenter, false);dropbox.addEventListener("dragover", dragover, false);dropbox.addEventListener("drop", drop, false);// 我们必须阻止dragenter和dragover事件的默认行为,这样才能出发drop事件function dragenter(e) {  e.stopPropagation();  e.preventDefault();}function dragover(e) {  e.stopPropagation();  e.preventDefault();}// 下面是drop函数:function drop(e) {  e.stopPropagation();  e.preventDefault();  // input标签是通过获取元素得到filesList,而拖拽是用了事件e的dataTransfer属性   var dt = e.dataTransfer;  var files = dt.files;  handleFiles(files);}

6.例子1:显示用户所选图片的缩略图

假设你正在开发下一个伟大的照片分享网站,并希望使用HTML5在用户上传他们图片之前进行缩略图预览。可以入前面所讨论的建立一个输入元素或者拖放区域,并调用一个函数,如下面的handleFiles函数。

function handleFiles(files) {    for (var i = 0; i < files.length; i++) {        var file = files[i];        var imageType = /^image\//;        if (!imageType.test(file.type)) continue;        var img = document.createElement("img");        img.classList.add("obj");        img.file = file;        // 假设"preview"是将要展示图片的div        preview.appendChild(img);        var reader = new FileReader();        reader.onload = (function(aImg) {            return function(e) {                aImg.src = e.target.result;            }        }(img);        reader.readerAsDataURL(file);    }}

这里我们循环处理用户选择的文件,查看每个文件的类型属性,看看这是否是一个图像文件(通过一个正则表达式匹配字符串“image.*”)。对于每个图片文件,我们创建一个新的img元素。CSS可以用于建立任何漂亮的边界,阴影,和指定图像的大小,所以,甚至不需要在这里完成。

每张图片我们添加一个obj类,让他们更容易的在DOM树中被找到。我们也在图片上添加了一个file属性来确认每张图片的 File,这样可以帮助我们在之后真正的上传工作时获取到图片。最后我们使用 Node.appendChild() 把缩略图添加到我们先前的文档区域中。

然后,我们建立了FileReader来处理图片的异步加载,并把它添加到img元素上。在创建新的FileReader对象之后,我们建立了onload函数,然后调用readAsDataURL()开始在后台进行读取操作。当图像文件的所有内容加载后,他们转换成一个 data: URL,传递到onload回调函数中。之后只需要把img元素的src属性设置为这个加载过的图像,就可以让图像的缩略图出现在用户的屏幕上。

7.使用对象URL

Gecko 2.0 (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)开始支持window.URL.createObjectURL()和window.URL.revokeObjectURL()两个DOM方法。
这两个方法创建简单的URL字符串对象,用于只想任何DOM File对象数据,包括用户电脑中的本地文件。

var objectURL = window.URL.createObjectURL(fileObj);

URL对象是File对象的一个字符串标志。每次调用window.URL.createObjectURL()的时候,都会创建一个唯一的URL对象,即是你已经为该文件创建了URL对象。这些对象都会被释放,如果想要主动释放,应该明确调用window.URL.revokeObjectURL()。

8.例子2:使用对象URL来显示图片

<input type="file" id="fileElem" multiple accept="image/*"   style="display:none" onchange="handleFiles(this.files)"><a href="#" id="fileSelect">Select some files</a> <div id="fileList">  <p>No files selected!</p></div>window.URL = window.URL || window.webkitURL;var fileSelect = document.getElementById("fileSelect"),    fileElem = document.getElementById("fileElem"),    fileList = document.getElementById("fileList");fileSelect.addEventListener("click", function (e) {  if (fileElem) {    fileElem.click();  }  e.preventDefault(); // prevent navigation to "#"}, false);function handleFiles(files) {  if (!files.length) {    fileList.innerHTML = "<p>No files selected!</p>";  } else {    var list = document.createElement("ul");    for (var i = 0; i < files.length; i++) {      var li = document.createElement("li");      list.appendChild(li);      var img = document.createElement("img");      // 创建URL,并设置图片的源为表示文件的URL对象      img.src = window.URL.createObjectURL(files[i]);      img.height = 60;      img.onload = function(e) {        window.URL.revokeObjectURL(this.src);      }      li.appendChild(img);      var info = document.createElement("span");      info.innerHTML = files[i].name + ": " + files[i].size + " bytes";      li.appendChild(info);    }    fileList.appendChild(list);  }}

9.例子3:上传用户选择的文件

可以异步的将用户所选择的文件上传到服务器上(比如一张图片)。

创建上传任务
紧接上述代码,每一个缩略图img.file = file,而都有个.object类,所以有:

function sendFiles() {  var imgs = document.querySelectorAll(".obj");  for (var i = 0; i < imgs.length; i++) {    new FileUpload(imgs[i], imgs[i].file);  }}

实现文件上传
FileUpload函数接受两个参数,一个img元素,一个可以从中读取到图像数据的文件file。

function FileUpload(img, file) {    var reader = new FileReader();    // 创建一个活动指示器,用于显示相关的进度信息    this.ctrl = createThrobber(img);    var xhr = new XMLHttpRequest();    this.xhr = xhr;    var self = this;    // 设置XHR的upload progress监视器以新的百分比信息去更新活动指示器,并将其作为上传进度    this.xhr.upload.addEventListener("progress", function(e) {        if (e.lengthComputable) {            var percentage = Math.round((e.loaded * 100) / e.total);            self.ctrl.update(percentage);        }    }, false);    // 设置XHR的upload load事件句柄更新信息活动指示器的进度到100%后,自动消失    xhr.upload.addEventListener("load", function(e){          self.ctrl.update(100);          var canvas = self.ctrl.ctx.canvas;          canvas.parentNode.removeChild(canvas);    }, false);    // 调用open()方法上传    xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");    xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');    reader.onload = function(e) {        xhr.sendAsBinary(evt.target.result);    };    // FileReader对象可以把file转换成二进制字符串    reader.readAsBinaryString(file);}

异步上传

<html><head>    <title>dnd binary upload</title>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <script type="text/javascript">        function sendFile(file) {            var uri = "/index.php";            var xhr = new XMLHttpRequest();            var fd = new FormData();            xhr.open("POST", uri, true);            xhr.onreadystatechange = function() {                if (xhr.readyState == 4 && xhr.status == 200) {                    // Handle response.                    alert(xhr.responseText); // handle response.                }            };            fd.append('myFile', file);            // Initiate a multipart/form-data upload            xhr.send(fd);        }        window.onload = function() {            var dropzone = document.getElementById("dropzone");            dropzone.ondragover =               dropzone.ondragenter = function(event) {                event.stopPropagation();                event.preventDefault();            }            dropzone.ondrop = function(event) {                event.stopPropagation();                event.preventDefault();                var filesArray = event.dataTransfer.files;                for (var i=0; i<filesArray.length; i++) {                    sendFile(filesArray[i]);                }            }    </script></head><body>    <div>        <div id="dropzone"           style="margin:30px; width:500px; height:300px; border:1px dotted grey;">            Drag & drop your file here...        </div>    </div></body></html>

什么是Data URL

Data URL说实话就是把图片转换成base64编码的字符串形式,并存储在URL中,冠以mime-type。

1.Data URL基本原理

图片在网页中的使用方法通常是下面这种利用img标记的形式:

<img src="images/myimg.gif">

这种方式中,img标记的src属性指定了一个远程服务器上的资源。当王爷加载到浏览器中时,浏览器会针对每个外部资源都向服务器发送一次拉去资源请求,占用网络资源。大多数的浏览器都有一个并发请求数不能超过4个的限制。这意味着,如果一个网页里嵌入了过多得外部资源,这些请求会导致整个页面的加载延迟。而使用Data URL技术,图片以base64字符串格式嵌入到了页面中,与HTML称为一体,它的形式如下:

几乎所有的现代浏览器都支持Data
URL格式,包括火狐浏览器,谷歌浏览器,Safari浏览器,opera浏览器。IE8也支持,但有部分限制,IE9完全支持。

2.为什么Data URL是个好东西

Data URL能用在很多场合,跟传统的外部资源引用方式相比,它有如下独到的用处:

  • 当访问外部资源很麻烦或受限时(这个比较鸡肋)

  • 当图片是在服务器端用程序动态生成,每个访问用户显示的都不同时(场景较少)

  • 当图片的体积太小,占用一个HTTP会话不是很值得时(雪碧图可以出场了)

Data URL也有一些不适用的场合

  • Base64编码的数据体积通常是原数据的体积4/3,也就是Data URL形式的图片会比二进制格式的图片体积大1/3

  • Data URL形式的图片不会被浏览器缓存,这意味着每次访问这样页面时都被下载一次。这是一个使用效率方面的问题——尤其当这个图片被整个网站大量使用的时候。

      然而,Data URL这些不利的地方完全可以避免或转化。本文的重点就是要讨论这个问题。

3.在CSS里使用Data URL

当第一次看到Data URL的作用和用法时,你也许会很不疑惑,“为什么要麻烦的将图片转换成base64编码字符串,还要嵌入的网页中,将HTML代码弄的混乱不堪,甚至还会有性能上的问题。”

诚然,无法否认缓存在浏览器性能中的重要作用——如何能将Data URL数据也放入浏览器缓存中呢?
答案是:通过CSS样式文件。CSS中的url操作符是用来指定网页元素的背景图片的,而浏览器并不在意URL里写的是什么——只要能通过它获取需要的数据。所以,我们就有了可以将Data URL形式的图片存储在CSS样式表中的可能。而所有浏览器都会积极的缓存CSS文件来提高页面加载效率。

假设我们的页面里有一个很小的div元素,我们想用一种灰色的斜纹图案做它的背景,这种背景在当今的网站设计者中非常流行。传统的方法是制作一个3×3像素的图片,保存成GIF或PNG格式,然后在CSS的background-image属性中引用它的地址。而Data URL则是一种更高效的替代方法,就像下面这样。

    .striped_box  {            width: 100px;            height: 100px;            background-image: url("data:image/gif;base64,R0lGODlhAwADAIAAAP///8zMzCH5BAAAAAAALAAAAAADAAMAAAIEBHIJBQA7");            border: 1px solid gray;            padding: 10px;      }  

只要这个图片不是很大,而且不是在CSS文件里反复使用,就可以以Data URL方法呈现图片降低页面的加载时间,改善用户的浏览体验。

关于FileReader

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用FileBlob对象指定要读取的文件或数据。

File对象可以是来自用户在一个<input>元素上选择文件后返回的FileList对象,也可以是来自拖放操作生成的DataTransfer对象,还可以是来自在一个HTMLCanvasElement上执行mozGetAsFile()方法后返回结果。

1.属性

error

var error = instanceOfFileReader.error
在Gecko 2.0 beta 7 (Firefox 4.0 beta 7)之前,上述方法中所有的 Blob 参数都只能是一个 File 对象。
根据最新的 FileAPI 草案,现在的所有的 Blob 参数既可以是 Blob 对象也可以是一个 File 对象。

onload

当FileReader读取文件的方式为readAsArrayBuffer,readAsBinaryString,readAsDataURL或者readAsText的时候,会触发一个load事件。
从而可以使用FileReader.onload属性对该事件进行处理。

// 一个文件上传的回调 <input type="file" onchange="onChange(event)">function onChange(event) {  var file = event.target.files[0];  var reader = new FileReader();  reader.onload = function(event) {    // 文件里的文本会在这里被打印出来    console.log(event.target.result)  };  reader.readAsText(file);}

readyState

var state = instanceOfFileReader.readyState
常量名 值 描述
EMPTY 0 还没有加载任何数据.
LOADING 1 数据正在被加载.
DONE 2 已完成全部的读取请求.

result

var file = instanceOfFileReader.result

2.方法

abort()

该方法可以取消FileReader的读取操作,触发之后readyState为已完成。

instanceOfFileReader.abort();

readAsArrayBuffer()

FileReader提供的readASArrayBuffer()方法会开始读取指定的Blob或File对象。当读取操作完成的时候,readyState变成已完成(done),并触发loadend事件,同时result属性中将包含一个ArrayBuffer对象以表示所读取文件的数据。

instanceOfFileReader.readAsArrayBuffer(blob);

参数时即将被读取的Blob或File对象

readAsBinaryString()

该方法已被废除。

readAsDataURL()

该方法会读取指定的Blob或File对象,读取操作完成时,readyState会变成已完成(DONE),并触发loadend事件,同时result属性将包含一个data: URL格式的字符串(base64编码)以表示所读取文件的内容。

instanceOfFileReader.readAsDataURL(blob)

readAsText()

readAsText 方法可以将Blob或者File对象根据特殊的编码格式转化为内容(字符串形式)
这个方法是异步的,也就是说,只有当执行完成后才能够查看到结果,如果直接查看是无结果,并返回undefined,
也就是说必须要挂载实例下的onload或者onloadend的方法处理转化后的结果。
当转化完成后,readyState这个参数就会转换为done即完成态,event(“loadend”)挂载的事件就会被处罚,并可以通过事件返回的形参得到FileReader.result属性得到转化后的结果。

参考文献

  1. FileReader【MDN】
    https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
  2. 在Web应用中使用文件 【MDN】
    https://developer.mozilla.org/zh-CN/docs/Using_files_from_web_applications#%E4%BE%8B%E5%AD%90%EF%BC%9A%E6%98%BE%E7%A4%BA%E7%94%A8%E6%88%B7%E6%89%80%E9%80%89%E5%9B%BE%E7%89%87%E7%9A%84%E7%BC%A9%E7%95%A5%E5%9B%BE
  3. DATA URL简介以及DATA URL的利弊
    http://www.cnblogs.com/xuechenlei/p/5940371.html
0 0
原创粉丝点击