很多时候,在我们开发的过程中,除了上传文件,还经常会遇到下载文件这一需求,其实也不太能算是需求,毕竟能上传肯定要能下载嘛。对于其他的浏览器不是很清楚,如果是chrome的话,用a标签是无法下载图片,txt...等,因为浏览器可以解析这几种格式,点了之后就不是下载,而是变成预览。
  对于这个问题,网上很多互相抄来抄去的文章,大致给出了以下这几种方法:


  • a标签添加download属性,此方法在chrome下一点用都没有,图片,文本还是照样是预览
  • 使用form发送action,此方法没有尝试,不过应该也有兼容性问题
  • 使用iframe来获取,不熟悉,使用了之后没用,可能使用方法不对
  • 发送XMLHttpRequest,然后设置返回二进制文件,通过返回的创建一个临时url进行下载,此方法在本地开发可行,放上服务器后报错 Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided,大致意思就是这个方法不能这么使,但其实就是要这么使,网上文章给的解释都是一堆跟video的文件流有关的,一点用处都没有。其实不是这个方法有问题,而是你通过这种方式返回的blob在线上运行时有问题,具体输出看不到就没去深究,遇到这个问题,最简单的就是摒弃这个方法。
  • 使用插件downloadjs,不熟悉,而且也觉得没有必要,实现方式也是去发个请求改成二进制后下载下来,记得放弃它是因为看到一篇文章说这个插件不能支持中文路径,未使用,不知其是否可行。


  所以说,这几种方法局限性都很大,有的甚至就根本没有作用,经过自己设想了一下,开发过程中就一直使用axios来调接口,可不可以使用axios通过文件的url来下载文件呢,采用这个想法,试了一下,目前在chrome浏览器下表现正常,0 errors, 0 warnings,一样的道理,使用平常调取接口的方式,ajax应该也可以。这里注意可能会出现的跨域问题即可。具体使用如下:

import axios from "axios";
//下载文件
export function download(url) {
  return axios.get(url, { responseType: "blob" });
}

<template>
  <div>
    <!--...-->
    <!--省略其他非必要代码-->
    <i
      class="el-icon-download"
      style="color:#409eff;"
      onclick="downloadDocs(file)"
    ></i>
    <!--...-->
  </div>
</template>

<script>
  import { config } from "@/config";
  import { download } from "@/api/download";
  export default {
    data() {
      return {
        host: config.host,
      };
    },
    //...省略非必要代码
    methods: {
      //...省略非必要代码
      downloadDocs(file) {
        if (file.uploading) {
          this.$message({
            message: "文件上传中,请稍后",
            type: "warning",
          });
          return;
        }

        downloadDoc(this.host + file.url)
          .then((response) => {
            if (response.status == 200) {
              const content = response.data;
              // 创建a标签并点击, 即触发下载
              let url = window.URL.createObjectURL(new Blob([content]));
              let link = document.createElement("a");
              link.style.display = "none";
              link.href = url;
              link.setAttribute("download", file.name);
              // 模拟
              document.body.appendChild(link);
              link.click();
              // 释放URL 对象
              window.URL.revokeObjectURL(link.href);
              document.body.removeChild(link);
            } else {
              this.$message.error("获取文件失败!");
            }
          })
          .catch((err) => {
            this.$message.error(err);
          });
      },
    },
    //...省略非必要代码
  };
</script>

  通过此方法,目前已可以实现在chrome浏览器下点击后就把文件下下来,不会再把图片文本那些当成可以解析的格式从而变成预览。这个方法也就是在没有后端专门下载文件的接口下使用,如果是正常情况还是让后端放一个下载文件的接口可能会更好一点,至少不会暴露文件的url,而且可以在后端服务器上设置,使得文件更加不容易被不怀好意的人获取。此方法适用于想单纯通过前端去下载文件,同时记录下来也是为了更好的帮助遇到此问题的人儿们,当然,如果有更好的方法,欢迎大神们评论区留言,不吝赐教。

  • alipay
  • wechat

一个好奇的人