面试题:我现在上传图片的时候提前预览到图片怎么办?

2,229 阅读7分钟

今天我也来标题党一会,用“面试题”蹭一蹭热度,主要还行想深度剖析一下,文件上传,里面的门道。

1、在web上怎么实现文件上传

在我们使用的各种类库,框架中文件上传长相多样,百花齐放,但是归根结底还是离不开一个input标签,据我所知,所有的文件上传都是 html(如有别的方式请大佬指正) input标签 来实现上传,写法如下:

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

长相如下:

我们发现最原始的的上传,其实就就张这样,朴实无华,后来的那些框架和库,其实就是隐藏了这个朴实无华的input 而加入了很多华丽的样式,来操纵这个input

比如你看饿了么的这个上传改的相当的花里胡哨,那我们上传之后会得到什么呢?如下图:

其实这个input的dom对象下面会有个fileList对象,上传之后会存储当前这个文件的所有信息,实际上,这个fileLlist,就是一个blob对象,

什么是blob对象

Blob,Binary Large Object的缩写,代表二进制类型的大对象。Blob的概念在一些数据库中有使用到,例如,MYSQL中的BLOB类型就表示二进制数据的容器。在Web中,Blob类型的对象表示不可变的类似文件对象的原始数据,通俗点说,就是Blob对象是二进制数据的容器,用直观的方式去描述这个二进制数据 实际上这个fileList就是一个特殊的blob对象

blob如何使用呢?

构建一个Blob对象通常有三种方式:

1、通过Blob对象的构造函数来构建。

2、从已有的Blob对象调用slice接口切出一个新的Blob对象。

3、canvas API toBlob方法,把当前绘制信息转为一个Blob对象。

下面只看第一种的实现

//构造函数来构建
var blob = new Blob(array[optional], options[optional]);

构造函数,接受两个参数

第一个参数:为一个数据序列,可以是任意格式的值,例如,任意数量的字符串,Blobs 以及 ArrayBuffers。 第二个参数:用于指定将要放入Blob中的数据的类型(MIME)(后端可以通过枚举MimeType,获取对应类型

Blob对象的基本属性:

size :Blob对象包含的字节数。(只读)

type : Blob对象包含的数据类型MIME,如果类型未知则返回空字符串。

Blob对象的基本方法:

大文件分割 (slice() 方法),slice方法与数组的slice类似。

此时一个blob对象就创建好了,在上一部分中,我说fileList是个特殊的blob,你可以发现他其实是在blob的两大属性上加了几个别的属性,来具体的描述整个文件

blob有啥作用呢?

1、大文件上传

得益于blob的slice方法

当要上传大文件的时候,此方法非常有用,可以将大文件分割分段,然后各自上传,因为分割之后的 Blob 对象和原始的是独立存在的。

不过目前浏览器实现此方法还没有统一,火狐使用的是 mozSlice() ,Chrome 使用的是 webkitSlice() ,其他浏览器则正常的方式 slice()

//这里提供一个兼容写法
   function sliceBlob(blob, start, end, type) {
      type = type || blob.type;
      if (blob.mozSlice) {
          return blob.mozSlice(start, end, type);
      } else if (blob.webkitSlice) {
          return blob.webkitSlice(start, end type);
      } else {
          throw new Error("This doesn't work!");
      }

生成Blob链接,用于隐藏真实链接

某个时间开始我们打开调试工具去看各大视频网站的视频src会发现,它们统统变成了这样的形式。

这其实是为了防止盗链,而让后台传入的一段二进制流,我们在给包装成blob对象,存在内存中后,在给转成可以播放的链接,这样就有效防止了真是链接的泄露,接下来我们一步步深度剖析(可能有不对之处,请大佬随之批评指正)! 1、首先第一步,我们得有一个视频网址,然后,我们通过ajax获取 2、第二部后台得给这个链接转化成一个二进制的流,我们用blob对象,给他装进去, 3、用URL.createObjectURL方法,生成一个blob url 4、给这个blob url赋值到video的src上,浏览器就会自动解析地址,播放视频

废话少说,下上代码

       //创建XMLHttpRequest对象
        var xhr = new XMLHttpRequest();
        //配置请求方式、请求地址以及是否同步
        xhr.open('POST', '二进制流的地址', true);
        //设置请求结果类型为blob
        xhr.responseType = 'blob';
        //请求成功回调函数
        xhr.onload = function(e) {
            if (this.status == 200) {//请求成功
                //获取blob对象
                var blob = this.response;
                //获取blob对象地址,并把值赋给容器
                document.getElementById("video").src = URL.createObjectURL(blob);
            }
        };

上述代码有一个知识点:

URL.createObjectURL

URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。

也就是说使用这个方法去创建一个DOMstring 引用这这个内存中的二进制流,然后在赋值到video标签上去就能达到隐藏链接的目的

  var debug = { hello: "world" };
  var blob = new Blob([JSON.stringify(debug, null, 2)], { type: 'application/json' });
   console.log(blob)
   var url = URL.createObjectURL(blob)
   console.log(url)

如此真实的链接就会被隐藏,并且这个链接是会动态变化,他在被video解析之后指向的地址就是个二进制文件的空间地址,看不见摸不着

解析到此,回归正题

2、现在上传图片的时候提前预览到图片怎么办?

废话少说先上代码:

//html
 <input type=file>
//js
//拿到当前的input
  const input = document.querySelector('input[type=file]')
     //监听改变,如此能拿到文件上传的特殊的blob对象,上文介绍过
        input.addEventListener('change', () => {
            //函数被执行,说明已经上传了文件
            console.log(input.files)
             //new一个fileReader对象,至于为啥先卖个关子
            const reader = new FileReader()
            reader.readAsDataURL(input.files[0]) // input.files[0]为第一个文件
            //成功之后赋值
            reader.onload = () => {
                const img = new Image()
                img.src = reader.result
                document.body.appendChild(img)  // reader.result为获取结果
            }
        }, false)

效果如下,我们发现我图片还没调用接口上传到服务器呢,就已经能预览了

下面我们来说一下卖的这个关子

FileReader是啥?

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

下面我们开看看他的一台方法:

FileReader.readAsArrayBuffer()

开始读取指定的 Blob中的内容, 一旦完成, result 属性中保存的将是被读取文件的 ArrayBuffer 数据对象.

**FileReader.readAsBinaryString() **

开始读取指定的Blob中的内容。一旦完成,result属性中将包含所读取文件的原始二进制数据。

FileReader.readAsText()

开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个字符串以表示所读取的文件内容。

FileReader.readAsDataURL()

开始读取指定的Blob中的内容。一旦完成,result属性中将包含一个data: URL格式的Base64字符串以表示所读取文件的内容。这个是我们要用到的,因为他的表示方式能被img的src读取打印结果如下:

相信搞过前端的人都不陌生这些base64的字符串,其实就是一段能表示出来的二进制文件,至于为啥能解析成图片这里就不展开讲了,有兴趣自行百度,一堆答案

那有人又会问了FileReader.readAsDataURL 和URL.createObjectURL有啥区别呢?

FileReader.readAsDataURL 和URL.createObjectURL区别

1、返回值

  FileReader.readAsDataURL(blob)可以得到一段base64的字符串
  URL.createObjectURL(blob)得到的是当前文件的一个内存url

2、内存使用

 FileReader.readAsDataURL(blob)得到一段超长的base64的字符串,代表的是个二进制
  URL.createObjectURL(blob)得到的是一个blob开头url地址 指向的是这个二进制地址

3、内存清理

FileReader.readAsDataURL(blob)依照js垃圾回收机制自动从内存中清理
 URL.createObjectURL(blob)存在于当前document内,清除方式只有upload()事件或者revokeObjectURL手动清除

4、执行方式

FileReader.readAsDataURL(blob)通过回调的方式f返回,异步执行;
URL.createObjectURL(blob) 直接返回,同步执行;

5、多个文件

  FileReader.readAsDataURL(blob)同时处理多个文件时,需要一个文件对应一个FileReader对象;
  URL.createObjectURL(blob) 依次返回,没有影响;

更多详细区别参考:blog.csdn.net/qq_36671474…

总结

到这里啊顺利的实现一个提前预览图片的功能,核心就是利用前端的一些对象去将文件资源存存起来,不管存入内存或者字符串,然后读取即可,文章属于现学现卖,记录学习点滴,不对之处,还请大佬指正!