前端常用的下载方法整理

114 阅读3分钟

在日常开发过程中,经常会遇到下载业务,不同的场景下载方法也有所不同,现在就把遇到的下载方法整理如下

location.href 下载方式

这种方法最为简便

应用场景: 已知文件路径,可以用此方法下载

function download(url) {
    window.location.href = url;
}

iframe 下载方式

应用场景:

  • 已知文件路径
  • 需要知道是否下载成功
  • 需要知道是否下载完成
/**
 * iframe 下载
 * @param {*} url 下载路径
 * @param {Object} data 下载参数
 */
function iframeDownload(url, data) {
  const query = param(data)
  if (query) {
    url = url + '?' + query
  }
  const iframe = document.createElement('iframe')
  iframe.style.display = 'none'
  let loading = false

  function iframeLoad() {
    const win = iframe.contentWindow
    const doc = win.document
    if (win.location.href === url) {
      if (doc.body.childNodes.length > 0) {
        // response is error
      }
      iframe.parentNode.removeChild(iframe)
    } else {
      if (loading) {
        loading = false
        console.log('下载失败')
      }
    }
  }
  if ('onload' in iframe) {
    iframe.onload = iframeLoad
  } else if (iframe.attachEvent) {
    iframe.attachEvent('onload', iframeLoad)
  } else {
    iframe.onreadystatechange = function onreadystatechange() {
      if (iframe.readyState === 'complete') {
        iframeLoad()
      }
    }
  }
  iframe.src = ''
  document.body.appendChild(iframe)

  setTimeout(function loadUrl() {
    loading = true
    iframe.contentWindow.location.href = url
  }, 50)
}

blob/arraybuffer 下载方式

应用场景:

  • 未知文件路径
  • 服务端返回文件流
// blob 格式转化
function changeBlob(data) {
  return new Promise((resolve, reject) => {
    if (data instanceof Blob) {
      try {
        var b = new Blob([data])
        var r = new FileReader()
        r.readAsText(b, 'utf-8')
        r.onload = function() {
          var json = JSON.parse(r.result)
          resolve(json.message || json.msg)
        }
      } catch (error) {
        resolve(false)
      }
      
    } else {
      resolve(false)
    }
  })
}

function saveAs(obj, fileName) {
  let tmpa = document.createElement("a");
  tmpa.download = fileName || "下载";
  tmpa.href = URL.createObjectURL(obj);
  tmpa.click();
  setTimeout(function () {
    URL.revokeObjectURL(obj);
  }, 100);
}

// 常用文件格式
const typeMap = {
  xls: 'application/vnd.ms-excel',
  doc: 'application/msword',
  pdf: 'application/pdf',
  ppt: 'application/vnd.ms-powerpoint',
  rar: 'application/x-rar-compressed',
  zip: 'application/zip',
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  xml: 'text/xml'
}
function downloadFile(params) {
  const type = params.type
  const resType = params.resType || 'blob'
  const name = params.name || new Date().getTime()
  return service({
    url: params.url,
    method: params.method || 'post',
    data: params.data,
    params: params.params,
    responseType: resType
  }).then(res => {
    if (resType == 'arraybuffer') {
      
    } else if (resType == 'blob') {
      if (res.type == 'application/json') {
        return changeBlob(res).then(json => {
          console.log('下载失败', json)
          return Promise.reject(json)
        })
      }
    } else {
      return res
    }
    const b = new Blob([res], { type: typeMap[type] })
    saveAs(b, `${name}.${type}`)
    return res
  })
}

图片/文本下载 禁止打开

应用场景:

  • 已知文件路径
  • 文件格式为 图片或者文本
//  blob 方式
function downloadBlob(url, name="download") {
  function toDataURL(url) {
      return fetch(url).then((response) => {
              return response.blob();
          }).then(blob => {
              return URL.createObjectURL(blob);
          });
  }
  (async function() {
      const a = document.createElement("a");
      imgUrl = url+'?'+Math.random()
      a.href = await toDataURL(imgUrl);
      a.setAttribute('download', name)
      a.click();
  })()
}

//  XMLHttpRequest  方式
function downloadFetch(url, name="download") {
  var x = new XMLHttpRequest();
  x.open("GET", url, true);
  x.responseType = 'blob';
  x.onload = function (e) {
    var url = window.URL.createObjectURL(x.response)
    var a = document.createElement('a');
    a.href = url
    a.download = name
    a.click()
  }
  x.send();
}

合并zip下载

应用场景:

  • 已知文件流
  • 多个文件合并下载

jszip如何使用请移步

function download() {
    let zip = new JSZip(); 
    fileList.forEach((file) => {
        zip.file(filename, file);
    })
    zip.generateAsync({ type: "blob" }).then(function (blob) { 
        saveAs(blob, "下载.zip"); 
    }); }); 
}


ShowSaveFilePicker API 下载

ShowSaveFilePicker 是一个新的api,调用该方法后会显示允许用户选择保存路径的文件选择器。

image.png

showSaveFilePicker 方法支持一个对象类型的可选参数,可包含以下属性:

  1. excludeAcceptAllOption:布尔类型,默认值为 false。默认情况下,选择器应包含一个不应用任何文件类型过滤器的选项(由下面的 types 选项启用)。将此选项设置为 true 意味着 types 选项不可用。
  2. types:数组类型,表示允许保存的文件类型列表。数组中的每一项是包含以下属性的配置对象:
  • description(可选):用于描述允许保存文件类型类别。
  • accept:是一个对象,该对象的 keyMIME 类型,值是文件扩展名列表。
  1. suggestedName 文件名。
async function download3(blob, filename) {
  try {
    const handle = await window.showSaveFilePicker({
      suggestedName: filename,
      types: [
        {
          description: "text file",
          accept: {
            "text/plain": [".txt"],
          },
        },
        {
          description: "jpeg file",
          accept: {
            "image/jpeg": [".jpeg"],
          },
        },
      ],
    });
    const writable = await handle.createWritable();
    await writable.write(blob);
    await writable.close();
    return handle;
  } catch (err) {
    console.error(err.name, err.message);
  }
}

常用的一些方法

saveAs

function saveAs(obj, fileName) {
  let tmpa = document.createElement("a");
  tmpa.download = fileName || "下载";
  tmpa.href = URL.createObjectURL(obj);
  tmpa.click();
  setTimeout(function () {
    URL.revokeObjectURL(obj);
  }, 100);
}

base64转File

// base64 转 file
function base64toFile(base64, filename) {
	const arr = base64.split(',')
	const mime = arr[0].match(/:(.*?);/)[1]
	const suffix = mime.split('/')[1]
	const bstr = atob(arr[1])
	let n = bstr.length
	const u8arr = new Uint8Array(n)
	while (n--) {
		u8arr[n] = bstr.charCodeAt(n)
	}
	return new File([u8arr], `${filename}.${suffix}`, {
		type: mime
	})
}

文件流转base64

function readImg(file) {
  return new Promise((res) => {
    // 创建一个reader
    const reader = new FileReader()
    // 将图片2将转成 base64 格式
    reader.readAsDataURL(file)
    // 读取成功后的回调
    reader.onloadend = function() {
      const result = this.result
      res(result)
    }
  })
}

base64 转 blob

function base64Toblob(base64, imgType = 'image/jpeg') {
  const arr = base64.split(',')
  const bytes = atob(arr[1])
  const bytesLength = bytes.length
  const u8arr = new Uint8Array(bytesLength)
  for (let i = 0; i < bytes.length; i++) {
    u8arr[i] = bytes.charCodeAt(i)
  }
  const blob = new Blob([u8arr], { type: imgType })
  return blob
}

图片地址转base64

function getBase64Image(url, cb) {
  const image = new Image()
  const type = getImgType(url)
  image.src = url
  image.crossOrigin = '*' // 支持跨域图片
  image.onload = function() {
    const base64 = drawBase64Image(image, type)
    cb && cb(base64, type)
  }
}
function drawBase64Image(img, type = 'image/jpeg') {
  const canvas = document.createElement('canvas')
  canvas.width = img.width
  canvas.height = img.height
  const ctx = canvas.getContext('2d')
  ctx.drawImage(img, 0, 0, img.width, img.height)
  const dataURL = canvas.toDataURL(type)
  return dataURL
}

通过图片地址获取图片类型

const imgAcceptMap = {
  gif: 'image/gif',
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  tif: 'image/tiff',
  tiff: 'image/tiff',
  png: 'image/png'
}
// 通过图片地址获取图片类型
function getImgType(url) {
  const isUrl = /^(http|https|blob:http):\/\/.+$/.test(url)
  if (isUrl) {
    return imgAcceptMap[url.split('.').pop() || 'jpg']
  }
  const reg = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i
  const isBase64 = reg.test(url)
  if (isBase64) {
    return url.split(';')[0].split(':')[1] || 'image/jpeg'
  }
  return 'image/jpeg'
}

将 jpeg、png 转换为 webp

/**
 * 根据 jpeg、png File 文件对象,获取 webp 格式的 File 文件对象
 * @param {File} imageFile jpeg、png图片文件对象
 * @returns image/webp File
 */
const getWebpFileByImageFile = imageFile => {
  const base64ToFile = (base64, fileName) => {
    let arr = base64.split(','),
      type = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new File([u8arr], fileName, {
      type
    });
  };
  return new Promise((resolve, reject) => {
    const imageFileReader = new FileReader();
    imageFileReader.onload = function(e) {
      const image = new Image();
      image.src = e.target.result;
      image.onload = function() {
        const canvas = document.createElement("canvas");
        canvas.width = image.width;
        canvas.height = image.height;
        canvas.getContext("2d").drawImage(image, 0, 0);
        resolve(base64ToFile(canvas.toDataURL("image/webp"), imageFile.name.split(".")[0] + ".webp"))
      }
    }
    imageFileReader.readAsDataURL(imageFile)
  });
}