前端实现文件下载功能

更新日期: 2019-06-03阅读: 5.3k标签: 文件

1、通过window.open()打开新页面下载文件

window.open(`url`, '_self')

使用场景:下载excel文件,后端提供接口,接口返回的是文件流,可以直接使用window.open(),最简单的方式。

优点:最简洁;

弊端:当参数错误时,或其它原因导致接口请求失败,这时无法监听到接口返回的错误信息,需要保证请求必须是正确的且能正确返回数据流,不然打开页面会直接输出接口返回的错误信息,体验不好。


2、通过a标签打开新页面下载文件

export const exportFile = (url, fileName) => {
  const link = document.createElement('a')
  const body = document.querySelector('body')

  link.href = url
  link.download = fileName

  // fix Firefox
  link.style.display = 'none'
  body.appendChild(link)

  link.click()
  body.removeChild(link)
}

通过a标签下载的方式,同window.open()是一样的,唯一的优点是可以自定义下载后的文件名,a标签里有download属性可以自定义文件名。

弊端:同window.open()方式一样,无法监听错误信息。

问题:以上两种方式,当在下载.mp3格式,或者视频文件时,浏览器会直接播放该文件,而达不到直接下载的功能,此时,当下载音视频文件时无法使用以上两种方式。


3、通过文件流的方式下载

为了解决.mp3文件下载所带来的问题,通过ajax请求返回Blob对象,或者ArrayBuffer对象。

(1)、获取文件

如下:通过原生ajax请求返回Blob对象

const getBlob = (url) => {
  return new Promise((resolve) => {
    const xhr = new XMLHttpRequest()

    xhr.open('GET', url, true)
    xhr.responseType = 'blob'
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response)
      }
    }

    xhr.send()
  })
}

同样,也可以通过axios返回ArrayBuffer对象,同等作用

import axios from 'axios'
const getFile = url => {
    return new Promise((resolve, reject) => {
        axios({
            method:'get',
            url,
            responseType: 'arraybuffer'
        }).then(data => {
            resolve(data.data)
        }).catch(error => {
            reject(error.toString())
        })
    })
}

ArrayBuffer(又称类型化数组)

ArrayBuffer对象用来表示通用的、固定长度的原始二进制数据缓冲区。ArrayBuffer 不能直接操作,而是要通过类型数组对象或 DataView 对象来操作,它们会将缓冲区中的数据表示为特定的格式,并通过这些格式来读写缓冲区的内容。

Blob(Binary Large Object): 二进制大数据对象

Blob 对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File 接口基于Blob,继承了 blob 的功能并将其扩展使其支持用户系统上的文件。

注意:

如果下载文件是文本类型的(如: .txt, .js之类的), 那么用responseType: 'text'也可以, 但是如果下载的文件是图片, 视频之类的, 就得用arraybuffer或blob,更多详情请查看MDN

通过ajax请求的方式下载文件,可以解决第1、2中存在的弊端,当请求错误时或捕获到错误信息

(2)、保存文件

当获取到文件后,这时需要保存文件

const saveAs = (blob, filename) => {
  if (window.navigator.msSaveOrOpenBlob) {
    navigator.msSaveBlob(blob, filename)
  } else {
    const link = document.createElement('a')
    const body = document.querySelector('body')

    link.href = window.URL.createObjectURL(blob) // 创建对象url
    link.download = filename

    // fix Firefox
    link.style.display = 'none'
    body.appendChild(link)

    link.click()
    body.removeChild(link)

    window.URL.revokeObjectURL(link.href) // 通过调用 URL.createObjectURL() 创建的 URL 对象
  }
}
为了解决IE(ie10 - 11)和Edge无法打开Blob URL链接的方法,微软自己有一套方法window.navigator.msSaveOrOpenBlob(blob, filename),打开并保存文件,以上代码做了简单的兼容,navigator.msSaveBlob(blob, filename)是直接保存。注意,此为非标准功能,详情请查看相关文档

以下为完整代码

const getBlob = (url) => {
  return new Promise((resolve) => {
    const xhr = new XMLHttpRequest()

    xhr.open('GET', url, true)
    xhr.responseType = 'blob'
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response)
      }
    }

    xhr.send()
  })
}

const saveAs = (blob, filename) => {
  if (window.navigator.msSaveOrOpenBlob) {
    navigator.msSaveBlob(blob, filename)
  } else {
    const link = document.createElement('a')
    const body = document.querySelector('body')

    link.href = window.URL.createObjectURL(blob) // 创建对象url
    link.download = filename

    // fix Firefox
    link.style.display = 'none'
    body.appendChild(link)

    link.click()
    body.removeChild(link)

    window.URL.revokeObjectURL(link.href) // 通过调用 URL.createObjectURL() 创建的 URL 对象
  }
}

export const download = (url, filename = '') => {
  getBlob(url).then((blob) => {
    saveAs(blob, filename)
  })
}


4、如何实现批量下载,且打包文件

在第3点的基础上,如果要实现批量下载,那能做到的只是连续多次调用download方法,这样无法批量集中的下载文件。这个时候就需要能够对已获取到的文件流,进行一个打包的操作,然后一次下载完毕。

这时,需要用到两个库jszip 和 file-saver

完整的思路,通过ajax获取文件,然后用 jszip 压缩文件, 再用 file-saver 生成文件

(1)、获取文件

第3点中的第(1)点

(2)、打包文件

export const download = () => {
  const urls = ['url', 'url']   //需要下载的路径
  const zip = new JSZip()
  const cache = {}
  const promises = []
  urls.forEach((item) => {
    const promise = getBlob(item).then((data) => { // 下载文件, 并存成ArrayBuffer对象
      zip.file('下载文件名', data, { binary: true }) // 逐个添加文件
      cache[item.fileName] = data
    })
    promises.push(promise)
  })

  Promise.all(promises).then(() => {
    zip.generateAsync({ type: 'blob' }).then((content) => { // 生成二进制流
      FileSaver.saveAs(content, `打包下载.zip`) // 利用file-saver保存文件
    })
  })
}

相关jszip/file-saver更多详情

jszip:

https://github.com/Stuk/jszip

http://stuk.github.io/jszip/

file-saver:

https://github.com/eligrey/

贴上完整代码

/**
 * 获取文件
 * @param url
 * @returns {Promise<any>}
 */
const getBlob = (url) => {
  return new Promise((resolve) => {
    const xhr = new XMLHttpRequest()

    xhr.open('GET', url, true)
    xhr.responseType = 'blob'
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response)
      }
    }

    xhr.send()
  })
}

/**
 * 批量打包zip包下载
 * @param urlArr Array [{url: 下载文件的路径, fileName: 下载文件名称}]
 * @param filename zip文件名
 */
export const download = (urlArr, filename = '打包下载') => {
  if (!urlArr.length > 0) return
  const zip = new JSZip()
  const cache = {}
  const promises = []
  urlArr.forEach((item) => {
    const promise = getBlob(item.url).then((data) => { // 下载文件, 并存成ArrayBuffer对象
      zip.file(item.fileName, data, { binary: true }) // 逐个添加文件
      cache[item.fileName] = data
    })
    promises.push(promise)
  })

  Promise.all(promises).then(() => {
    zip.generateAsync({ type: 'blob' }).then((content) => { // 生成二进制流
      FileSaver.saveAs(content, `${filename}.zip`) // 利用file-saver保存文件
    })
  })
}

注意:

由于通过浏览器进行打包压缩,如果文件过大,或者下载的内容过多,可能导致浏览器崩溃。


原文来自:https://segmentfault.com/a/1190000019359452


链接: https://www.fly63.com/article/detial/3520

h5移动端实现图片文件上传

PC端上传文件多半用插件,引入flash都没关系,但是移动端要是还用各种冗余的插件估计得被喷死,项目里面需要做图片上传的功能,既然H5已经有相关的接口且兼容性良好,当然优先考虑用H5来实现。

前端使用js读取文件操作

首先我们定义一个input标签type=file、然后我们定义一个jsReadFiles的方法将文件作为参数;当上传文件的时候读取这个文件。图片上传成功,只是图片路径变成了base64编码的形式。

HTML5实现文件读取、编辑、保存

HTML5读取文件主要利用的就是FileReader这个API,它的使用需要从一个构造函数开始,保存文件的关键是生成文件对象,可以使用URL.createObjectURL()方法实现,该方法能返回给定对象的URL,用在<a>标签的href属性上就可以创建可下载的文件链接。

血淋淋的事实告诉你:你为什么不应该在JS文件中保存敏感信息

在JavaScript文件中存储敏感数据,不仅是一种错误的实践方式,而且还是一种非常危险的行为,长期以来大家都知道这一点。

在js文件中引入另一个js文件的实现方法总汇

比如我写了一个JS文件,这个文件需要调用另外一个JS文件,该如何实现呢?这篇文章主要介绍:在js文件中引入另一个js文件的实现

使用HTML5来实现本地文件读取和写入

最近有这样一个需求,就是在HTML页面中有个按钮导出,点击它,将构造一个文档并存储到本地文件系统中。另外还有个按钮,点击它,从本地文件系统中读取一个文件并对内容进行分析。

lock文件_我们为什么需要 lock 文件

从 Yarn 横空出世推出 lock 文件以来,已经两年多时间了,npm 也在 5.0 版本加入了类似的功能,lock 文件越来越被开发者们接收和认可。本篇文章想从前端视角探讨一下我们为什么需要 lock 文件,以及它的一些成本与风险,当然其中一些观点对于后端也是适用的

什么是断点续传?前端如何实现文件的断点续传

什么是断点续传?就是下载文件时,不必重头开始下载,而是从指定的位置继续下载,这样的功能就叫做断点续传。前端通过FileList对象获取到相应的文件,按照指定的分割方式将大文件分段,然后一段一段地传给后端,后端再按顺序一段段将文件进行拼接。

form表单文件上传_multipart/form-data文件上传

form表单的enctype属性:规定了form表单数据在发送到服务器时候的编码方式.。application/x-www-form-urlencoded:默认编码方式,multipart/form-data:指定传输数据为二进制数据,例如图片、mp3、文件,text/plain:纯文本的传输。空格转换为“+”,但不支持特殊字符编码。

使用HttpClient发送文件流到服务器端

适用场景: 网络绝对路径的URL文件或图片,不存储到本地,转换成stream,直接使用HTTPClient传送到SpringBoot的服务端,将文件存储下来,并返回一个文件地址。目前分层架构的系统越来越多这种需求,所以记录下来以备不时之需。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!