blob 下载文件type是否必须设置

发布时间 2023-12-12 14:11:12作者: shayloyuki

又遇到了一件鬼打墙的事,欲哭无泪。

1

几天前,有个bug:blob文件下载,如果下载非txt文件,比如图片、xlsx,下载后的文件无法正确显示。

image

    // 下载文件
    async download(row, prop) {
      const res = await resourceDownload(row[prop.field + "fileId"]);
      // res 为 blob 类型
      const blob = new Blob([res], { type: "text/plain;charset=utf-8" });
      this.$download.saveAs(blob, row[prop.field]);
    },

于是根据文件类型设置了不同的 mime type,就可以正常显示下载的文件了。

    // 下载文件
    async download(row, prop) {
      const { id, type, name } = row[prop.field];
      switch (type) {
        case "jpg":
          this.blobType = "image/jpeg";
          break;
        case "png":
          this.blobType = "image/png";
          break;
        case "xlsx":
          this.blobType =
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
          break;
        case "txt":
          this.blobType = "text/plain;charset=utf-8";
          break;
      }
      const res = await resourceDownload(id);
      // res 为 blob 类型
      const blob = new Blob([res], {
        type: this.blobType,
      });
      this.$download.saveAs(blob, name);
    },

2

但是今天接到新需求,如果下载 json 文件,下载后还是无法正确显示。如果手动把 json 的 mime type 加上,这样每次新增其他类型的文件,就要手动修改代码,太过麻烦,需要一个一劳永逸的办法。

    // 下载文件
    async download(row, prop) {
      const { id, type, name } = row[prop.field];
      switch (type) {
	// 新增 json
	case "json":
          this.blobType = "application/json";
          break;
        case "jpg":
          this.blobType = "image/jpeg";
          break;
        case "png":
          this.blobType = "image/png";
          break;
        case "xlsx":
          this.blobType =
            "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
          break;
        case "txt":
          this.blobType = "text/plain;charset=utf-8";
          break;
      }
      const res = await resourceDownload(id);
      // res 为 blob 类型
      const blob = new Blob([res], {
        type: this.blobType,
      });
      this.$download.saveAs(blob, name);
    },

于是发现,对于非txt的文件可以统一设置为 application/octet-stream, txt文件设置为 text/plain;charset=utf-8

    // 下载文件
    async download(row, prop) {
      const { id, type, name } = row[prop.field];
      switch (type) {
      	case "txt":
          this.blobType = "text/plain;charset=utf-8";
          break;
        default:
          this.blobType = "application/octet-stream";
          break;
      }
      const res = await resourceDownload(id);
      // res 为 blob 类型
      const blob = new Blob([res], {
        type: this.blobType,
      });
      this.$download.saveAs(blob, name);
    },

但是测试时发现,txt 文件设置为 application/octet-stream 也能正确下载显示;甚至不设置 type 也能正确下载显示任何类型的文件。

    // 下载文件
    async download(row, prop) {
      const { id, name } = row[prop.field];
      const res = await resourceDownload(id);
      // res 为 blob 类型
      const blob = new Blob([res]);
      this.$download.saveAs(blob, name);
    },

真的是芭比Q了。陷入了鬼打墙。

我知道肯定是哪里出了问题,要么是测试时没有遵循 "同一对象原则",要么是记忆出现了混乱。

参考链接

  1. Vue 之 new Blob() 文件流下载文件不同文件类型的 type 值整理
  2. MimeType对照表 application/octet-stream