前端文件下载和文件读取方法研究

06-28 1655阅读

前言

之前因为工作需要对geojson进行数据处理,在操作的过程中我就发现,自己手动去下载和读取文件就很麻烦。因此我就想在这里探讨一下如何在前端实现文件自动读取和下载。

一、文件读取

读取文件功能的技术点其实主要有两个:一是元素 ; 二是 FileReader类型。前者的作用是在浏览器中实现“文件选择”这样一个交互动作;后者的作用则是读取和解析所选择的文件。

1.文件选择器元素

元素的作用是允许用户从他们的设备中选择一个或多个文件。选择后,这些文件可以通过JS代码和文件API进行操作。

(1)基本示例

 

前端文件下载和文件读取方法研究

点击之后就会打开如下的窗口:

前端文件下载和文件读取方法研究

(2)获取文件

点击元素可以打开一个窗口选择文件,那么所选择的文件我们如何拿到呢?

这个问题其实又可以拆解成两个问题,什么时候拿?和 在哪儿拿?

首先回答什么时候拿的问题,当元素上传一个文件时会触发change事件,因此在onchange事件处理程序中去拿,就可以保证此时的文件是存在的,是可以拿的到的。

再回答在哪儿拿的问题,所选择的文件被保存在 元素的 files属性上,其属性值是一个FileList,FileList是一个类数组结构,其中的每个元素都是File对象,我们可以通过方括号或item()读取其中的元素。

所以这时我们就可以尝试去获取一个文件了:


  
    
    
        function readFile(event) {
            const file = event.target.files[0]
            console.log(file);
        }
    
  

前端文件下载和文件读取方法研究

上面打印的就是拿到的文件,它是一个File对象,后续我们还要通过 FileReader 去读取他。

(3)附加属性

元素还有一些附加属性,这些属性可以帮助我们拓展文件读取功能。

首先是multiple属性,它可以允许选择多个文件。


  
    
    
        function readFile(event) {
            const file = event.target.files[0]
            console.log(event.target.files);
        }
    
  

添加了multiple属性后,我们在选择窗口中就可以选择多个文件了

前端文件下载和文件读取方法研究

此时,flies属性中的文件也就有多个

前端文件下载和文件读取方法研究

另一个重要的附加属性是accept,它可以指定文件input接收的文件类型,从而实现“限制可接收的文件类型”的功能。accept属性值是一个字符串,这个字符串是一个以逗号为分隔的唯一文件类型说明符列表。

那么这个所谓的“唯一文件类型说明符”该怎么写呢?它其实是包括以下的这些形式:

  • 一个以英文句号(“.”)开头的合法的不区分大小写的文件名扩展名。例如:.jpg、.pdf 或 .doc。
  • 一个不带扩展名的 MIME 类型字符串。
  • 字符串 audio/*,表示“任何音频文件”。
  • 字符串 video/*,表示“任何视频文件”。
  • 字符串 image/*,表示“任何图片文件”。

    因此,假如我们的文件选择器只需要读取图片,包括标准的图片格式和 PDF 文件,那么大概是这样的:

     
    

    此时所打开的选择窗口中就只剩下我们所限定类型的文件了。

    前端文件下载和文件读取方法研究

    2.使用FileReader解析文件

    通过文件选择器我们可以在项目中拿到文件,但此时的文件是 File类型的,这种状态的文件我们无法直接处理和使用。这时就需要使用FileReader解析文件。

    (1)基本使用

    FileReader使用分为三步:

    第一步:实例化一个文件读取器

    const reader = new FileReader()

    第二步:调用文件读取器的某个读取方法,开始读取文件

    reader.readAsText(file ,'UTF-8') //这个方法可以将文件读取为字符串

    第三步: 侦听读取器的onload事件或者onloadend, 当文件读取完毕后,会触发这两个事件。在事件处理程序中通过读取器的result属性可以拿到读取的结果

    reader.onload = function(){
      this.result //读取的结果
    }

    读取一个txt文件

    此时我们就可以尝试去读取一个文件。首先创建如下的txt文件。

    前端文件下载和文件读取方法研究

    可以读取出txt文件中的内容

    前端文件下载和文件读取方法研究

    
      
        
        
          function readFile(event) {
            const file = event.target.files[0]
            const reader = new FileReader()
            reader.readAsText(file ,'UTF-8')
            reader.onload = function () {
              console.log('读取的结果', this.result)
            }
          }
        
      
    

    (2)读取方法介绍

    FileReader的读取方法可以帮助我们将Bobl、file文件转换为其它的格式。常用的读取方法有三个:

    • readAsArrayBuffer,将文件读取为ArrayBuffer数据对象
    • readAsDataURL,将文件读取为一个URL
    • readAsText,将文件读取为字符串

      readAsArrayBuffer方法所转化的结果是一个ArrayBuffer对象是一个用于存储二进制数据的内存,对于

      ArrayBuffer我还不是很理解,也不知道在什么情况下使用,等以后研究明白了再做介绍。readAsText在我写的读取文件的工具中就使用了这个方法,让它帮助我读取geojson文件,目前感觉这个方法是最实用的。readAsDataURL方法我看到有的文章中用它来读取图片,下面我就来尝试一下。

      读取图片并显示

      
         
        
          
          
      function readImg(event) { const file = event.target.files[0] const reader = new FileReader() reader.readAsDataURL(file) reader.onloadend = function () { console.log('读取的结果', this.result) document.images[0].src = this.result } }

      前端文件下载和文件读取方法研究

      二、文件下载

      文件下载也分为两步,第一步是创建一个元素(当元素设置了download属性后,浏览器就会将链接的 URL 视为下载资源);第二步是将我们的数据(或文件)转换为URL 。

      1.文件下载链接

      元素的download属性可以将连接变为文件下载链接。download属性的值为一个字符串,这个字符串就是fileName即下载文件的文件名。

       
      

      2.文件转URL

      由于我是需要将js中的数据下载为json文件,所以这里就讨论一下 “如何将js数据转换为url”。

      (1)FileReader方案

      首先将数据转换为Bolb对象,Blob对象接收两个参数:

      • Array,一个可迭代对象,包含 ArrayBuffer、TypedArray、DataView、Blob、字符串或者任意这些元素的混合。
      • Options,一个对象,其中可以指定以下属性。
          • type,将会被存储到 blob中的数据的 MIME 类型;
          data = JSON.stringify(data)
          const blob = new Blob([data], { type: 'Application/json' })

          然后再通过FileReader对象的readAsDataURL方法转换为URL

          const reader = new FileReader()
          reader.onload = function () {
            a.href = this.result //读取到的URL
            a.click()
          }
          reader.readAsDataURL(blob)

          (2)URL.createObjectURL方案

          也可以直接使用URL.createObjectURL方法将 blob对象转换为URL。(为了获得最佳性能和内存使用状况,建议在URL对象使用完毕后,通过URL.revokeObjectURL方法将其释放)

          // 创建blob对象
          data = JSON.stringify(data)
          const blob = new Blob([data], { type: 'Application/json' })
          // 将blob转换为URL对象
          const url = URL.createObjectURL(blob)
          // 设置url并点击下载
          a.href = url
          a.click()
          // 释放URL对象
          URL.revokeObjectURL(url)

          三、实现一个先文件读取后文件下载的小工具

          为了方便工作中的数据处理,我编写了一个小工具,它可以读取geojson文件,然后处理其中的数据,最后将数据下载为json文件。

          1.读取文件函数

          function readFile() {
            return new Promise((resolve) => {
              const fileElement = document.createElement('input')
              fileElement.type = 'file'
              fileElement.multiple = true
              fileElement.accept = ".json,.geojson"
              fileElement.onchange = (event) => {
                // 获取文件
                let fileArr = Array.from(event.target.files)
                Promise.all(
                  fileArr.map(
                    (file) =>
                      new Promise((success) => {
                        // 实例化一个文件读取器对象
                        const reader = new FileReader()
                        // 让读取器开始读取
                        reader.readAsText(file)
                        // 当文件读取完成后处理数据
                        reader.onload = (e) => {
                          const data = JSON.parse(e.target.result)
                          success(data)
                        }
                      })
                  )
                ).then((res) => {
                  resolve(res)
                })
              }
              fileElement.click()
            })
          }

          读取文件基本还是按照之前介绍的思路进行实现的,这里重点介绍一下我在实现这个readFile函数时遇到一个难点。

          为了提高函数的泛用性,我希望可以将读取到的文件数据return出去。为什么return会成为一个问题能?因为实际上这个函数中的文件读取是一组复杂的异步任务。

          前端文件下载和文件读取方法研究

          于是我使用期约来处理这里的异步任务:

          function readFile() {
            return new Promise((resolve) => {
              // 第一个Promise用于处理文件选择器的change事件
              // ......
                //Promise.all()用于实现多个文件的读取操作并行,保证能够拿到所有文件的数据
                Promise.all(
                  fileArr.map(
                    (file) =>
                      new Promise((success) => {
                        // ......
                        //  第二个Promise用于处理FileReader的load事件
                          success(data)
                      })
                  )
                ).then((res) => {
                  resolve(res)
                })
            })
          }

          2.下载文件函数

          //下载文件
          function download(files) {
            if (!files) return alert('请先读取一个文件')
            files.forEach((el) => {
              const a = document.createElement('a')
              a.download = el.name || getNowTime()
              const fileData = JSON.stringify(el.data)
              const blob = new Blob([fileData], { type: 'Application/json' })
              a.href = URL.createObjectURL(blob)
              a.click()
            })
            alert('文件下载成功')
          }

          下载文件的函数就很简单了,这里我也就不过多介绍了。

          3.完整代码

          index.html

          
            
              
              
              
              Document
              
              
              
              
            
            
              读取
              处理数据
              下载
              
                let files
                document.getElementById('read').addEventListener('click', () => {
                  readFile().then((data) => {
                    console.log(data)
                    files = data
                    alert('文件读取成功')
                  })
                })
                document.getElementById('handle').addEventListener('click', () => {
                  handleData(files).then((res) => {
                    console.log(res)
                    files = res
                    alert('数据处理成功')
                  })
                })
                document.getElementById('download').addEventListener('click', () => {
                  download(files)
                })
              
            
          

          File.js

          function readFile() {
            return new Promise((resolve) => {
              const fileElement = document.createElement('input')
              fileElement.type = 'file'
              fileElement.multiple = true
              fileElement.accept = ".json,.geojson"
              fileElement.onchange = (event) => {
                // 获取文件
                let fileArr = Array.from(event.target.files)
                Promise.all(
                  fileArr.map(
                    (file) =>
                      new Promise((success) => {
                        // 实例化一个文件读取器对象
                        const reader = new FileReader()
                        // 让读取器开始读取
                        reader.readAsText(file)
                        // 当文件读取完成后处理数据
                        reader.onload = (e) => {
                          const data = JSON.parse(e.target.result)
                          success(data)
                        }
                      })
                  )
                ).then((res) => {
                  resolve(res)
                })
              }
              fileElement.click()
            })
          }
          //下载文件
          function download(files) {
            if (!files) return alert('请先读取一个文件')
            files.forEach((el) => {
              const a = document.createElement('a')
              a.download = el.name || getNowTime()
              const fileData = JSON.stringify(el.data)
              const blob = new Blob([fileData], { type: 'Application/json' })
              a.href = URL.createObjectURL(blob)
              a.click()
            })
            alert('文件下载成功')
          }
          // 获取当前时间
          function getNowTime() {
            var date = new Date() //时间戳为10位需*1000,时间戳为13位的话不需乘1000
            var Y = date.getFullYear() + '-'
            var M =
              (date.getMonth() + 1  
          

          参考资料

          1. 纯前端下载json文件-CSDN博客
          2. 【前端下载文件流详解】前端实现多种类型文件(word,excel,pdf,rar,zip等)的下载,接口返回文件流形式(附源码)-CSDN博客
          3. js 读取文件内容_前端读取本地文件-CSDN博客
          4. - HTML(超文本标记语言) | MDN"> - HTML(超文本标记语言) | MDN
          5. FileReader - Web API 接口参考 | MDN
          6. FileReader.readAsDataURL() - Web API 接口参考 | MDN
          7. :锚元素 - HTML(超文本标记语言) | MDN">:锚元素 - HTML(超文本标记语言) | MDN
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]