聊聊文件图片预览的两种方法

时间:2021-1-8 作者:admin

在我们的前端业务中,经常需要上传图片并且预览出一张略缩图,在以前的实现中,往往需要借助后端的力量,在上传完毕后获取对应资源的链接,再在前端展示出来。这种方法本质上是”先上传,后预览”,违背了我们的初衷。为此,我们可以通过特定的web API来实现这一功能。

今天要介绍的两种方法,分别是File ReaderURL.createObjectURL()

一、上传文件之前的样式优化

在MDN中有这样一段描述:

You can hide the admittedly ugly file <input> element and present your own interface for opening the file picker 
and displaying which file or files the user has selected. You can do this by styling the input element with 
display:none and calling the click() method on the <input> element.

这段话的意思,除了吐槽html中原始的file <input>控件着实难看,也给出了实现一个自定义样式的文件上传控件的思路:

  1. 先将原始的input空间利用display:none在页面中隐藏。
  2. 自定义一个上传控件,它可以是一个按钮,a标签或是任何你希望的元素
  3. 在自定义的上传控件上绑定一个事件,在此事件的回调中获取原始的input元素,并手动调用input元素的click方法
  4. 在input元素上绑定一个onchange事件,用来处理选择文件后的回调函数。

下面,我将利用Vue代码实现这个功能:

<template>
        <input ref="file" @change="fileChange" type="file" multiple accept=".csv" style="display: none"/>
        <el-button  type="primary" size="small" @click="clickTrigger">选择</el-button>
</template>
<script>
  export default {
      methods:{
          clickTrigger(){
              this.$refs.file.click()
          }
          fileChange(ev){
              let newFiles = Object.values(ev.target.files)
                newFiles.forEach(newFile => {
                   // 对每个newFile做点事情
                })
          }
    }
  }
</script>

二、用FileReader实现图片预览

引用MDN上的原话:

FileReader 对象异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。
其中File对象可以是来自用户在一个`<input>`元素上选择文件后返回的FileList对象,也可以来自拖放操作生成的 `DataTransfer`对象,
还可以是来自在一个`HTMLCanvasElement`上执行`mozGetAsFile()`方法后返回结果。

在我们这篇文章中,先只考虑第一种情况,即用户选择文件后返回的FileList对象。让我们来熟悉一下FileReader:

首先,FileReader是一个类,返回一个fileReader实例,利用这个实例即可读取FileList里的File对象。在fileReader实例上,有一个readAsDataURL()方法,该方法读取指定的File对象,在读取文件完毕后,可以在fileReader的onload事件里获取返回结果。利用readAsDataURL方法,获取的返回结果是一个data: URL格式的Base64字符串,这个字符串可以直接放置在<img>标签的src属性中,这样一来,就得到了前端预览图片的第一种思路。

以下是代码实现:

    <template>
        <input ref="file" @change="fileChange" type="file" multiple accept=".csv" style="display: none"/>
        <el-button  type="primary" size="small" @click="clickTrigger">选择</el-button>
        <div ref='thumbnails'></div>
    </template>
<script>
  export default {
      data() {
        return {
            dataSrc:''
        }
    },
      methods:{
          clickTrigger(){
              this.$refs.file.click()
          }
          fileChange(ev){
              let newFiles = Object.values(ev.target.files)
                newFiles.forEach(newFile => {
                   let fr = new FileReader()
                   fr.readAsDataURL(newFile)
                   fr.onload = function(e) {
                        let dataURL = e.target.result;
                        let img = new Image()
                        img.src = dataURL
                        this.refs.thumbnails.appendChild(img)
                   }
                })
          }
    }
  }
</script>

三、利用URL.createObjectURL()实现图片预览

先来介绍一下URL这个类。URL类用来解析和构造url,在URL类中传入一个url字符串,将返回一个实例,这个实例中包含能让你轻松访问和修改该url的属性。

该类的兼容性不佳,IE10以上的版本中只支持createObjectURL和revokeObjectURL两个静态方法。不过对于我们今天的主题来说,URL.createObjectURL是勉强合格的(兼容IE10及以上)

不同于异步的FileReader,该方法同步返回一个带Hash的url,并且,URL对象的生命周期是和document绑定的,如果我们不手动调用revokeObjectURL清除这部分内存,它将存活至该页面关闭。

下面给出实现代码:

<template>
        <input ref="file" @change="fileChange" type="file" multiple accept=".csv" style="display: none"/>
        <el-button  type="primary" size="small" @click="clickTrigger">选择</el-button>
        <div ref='thumbnails'></div>
    </template>
<script>
  export default {
      data() {
        return {
            dataSrc:''
        }
    },
      methods:{
          clickTrigger(){
              this.$refs.file.click()
          }
          fileChange(ev){
              let newFiles = Object.values(ev.target.files)
                newFiles.forEach(newFile => {
                  let url = URL.createObjectURL(item)
                 let image = new Image()
                 image.src = url
                 this.refs.thumbnails.appendChild(image)
                 image.onload = function() {
                      URL.revokeObjectURL(this.src)
                  }
                })
          }
    }
  }
</script>

四、两种方法的差异

  1. FileReader的结果是异步返回的,而createObjectURL是同步的
  2. 返回结果不同,FileReader返回的是一个base64编码的字符串,一般来说体积较大;而createObjectURL返回的是一个带hash的url,相对前者来说较小
  3. 生命周期不同,FileReader返回结果后会自动释放内存,而createObjectURL需要手动释放内存
  4. createObjectURL出来用来预览图片,还可以获取pdf,video等其他格式的文件。
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。