那么轻,那么重:图片下载与压缩包下载

0. 缘起

下载,不同文本流有不同下载方式。

1. 图片下载

我直接嗯用以前下载图片的方法,改了下文本流的获取和下载名就敢直接用。结果,呃,直接文件损坏无法打开。

    //导出图片
    exportPic() {
      //canvas的toDataURL()方法返回一个包含图片展示的数据URL
      const dataURL = this.canvas.toDataURL({
         this.canvas.width,
        height: this.canvas.height,
        left: 0,
        top: 0,
        format: "png",
      });
      //创建一个a标签,指向图片的URL地址,
      //点击即可下载名为canvas.png的图片
      const link = document.createElement("a");
      link.download = "canvas.png";
      link.href = dataURL;
      //当前文档的Body对象上挂载一个元素,此处为a标签
      //模拟a标签的点击,下载后移除该元素
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    },

2. 压缩包下载的正确操作

2.1 Axios

用的axios,就从它的封装方法里面看。因为涉及到response有没有被更改

const instance = axios.create({
  baseURL,
  timeout: requestTimeout,
  headers: {
    "Content-Type": contentType,
  }
});

instance.interceptors.response.use(
  (response) => {
    if (loadingInstance) loadingInstance.close();

    const { data, config } = response;
    console.log('response: ', response);
    const { code, message } = data;

    let successFlag = response.status;
    // 操作正常Code数组
    const codeVerificationArray = isArray(successCode)
      ? [...successCode]
      : [...[successCode]];
    // 是否操作正常 Old Version
    // if (codeVerificationArray.includes(code)) {
      // New Version ***
    if (successFlag === 200) {
        // Special Blob Type
      if (response.config.responseType === 'blob') {
        return response
      } else {
        return data;
      }
    } else {
      console.log(code, message)
      handleCode(code, message);
      return Promise.reject(
        "请求异常拦截:" +
        JSON.stringify({ url: config.url, code, message }) || "Error"
      );
    }
  },
  (error) => {
    if (loadingInstance) loadingInstance.close();
    const { response, message } = error;
    if (error.response && error.response.data) {
      const { status, data } = response;
      handleCode(status, data.message || message);
      return Promise.reject(error);
    } else {
      let { message } = error;
      if (message === "Network Error") {
        message = "后端接口连接异常";
      }
      if (message.includes("timeout")) {
        message = "后端接口请求超时";
      }
      if (message.includes("Request failed with status code")) {
        const code = message.substr(message.length - 3);
        message = "后端接口" + code + "异常";
      }
      Vue.prototype.$baseMessage(message || `后端接口未知异常`, "error");
      return Promise.reject(error);
    }
  }
);

2.2 Response

注意当成功的时候,之前是直接返回了response里面的data,而我们在获取文件流,要注意responseType得是blob

  // `responseType` 表示服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream'
  responseType: 'json', // 默认的

使用说明 · Axios 中文说明 · 看云 (kancloud.cn)

比如这里,我的下载压缩包方法

// Download MagicBoxVersion 
export function downloadMagicBoxEdition(id) {
  return request({
    url: `/.../export?id=${id}`,
    method: 'POST',
    baseURL: baseURLB,
    responseType: 'blob',
  })
}

2.3 Data

到这一步在下载按钮的download函数里,传个值过去就能获得对应的文件流了。接下来就是如何下载:

    handleExport(row) {
      downloadMagicBoxEdition(row.id).then((res) => {
        console.log(res);
        let blob = new Blob([res.data], {
          type: "application/octet-stream;charset=utf-8",
        }); // res.data就是接口返回的文件流了
        let name = res.headers["content-disposition"];
        let fileName = `${decodeURIComponent(
          name.split(";")[1].split("=")[1]
        )}`.replace("zip", ".zip");
        if ("download" in document.createElement("a")) {
          // 非IE下载
          const elink = document.createElement("a");
          elink.download = fileName;
          elink.style.display = "none";
          elink.href = URL.createObjectURL(blob);
          document.body.appendChild(elink);
          elink.click();
          URL.revokeObjectURL(elink.href); // 释放URL 对象
          document.body.removeChild(elink);
        } else {
          // IE10+下载
          navigator.msSaveBlob(blob, fileName);
        }
      });
    },

2.4 Title

上面有个fileName,文件名,后端用content-disposition传了过来。

filename

后面是要传送的文件的初始名称的字符串。这个参数总是可选的,而且不能盲目使用:路径信息必须舍掉,同时要进行一定的转换以符合服务器文件系统规则。这个参数主要用来提供展示性信息。当与 Content-Disposition: attachment 一同使用的时候,它被用作"保存为"对话框中呈现给用户的默认文件名。

Content-Disposition - HTTP | MDN (mozilla.org)

        let name = res.headers["content-disposition"];        
		let fileName = `${decodeURIComponent(
          name.split(";")[1].split("=")[1]
        )}`.replace("zip", ".zip");

download函数里面的这段,显然是从头里面取出传递过来的文件名。

PS: 这里一定要注意第一步axios里面封装axios所传递的response是否是完全体,一般都传的response.data,是无法获取到header部分的!

3. 函数化的下载方法

就是1个下载来res,另一个处理res

/**
 * 下载文件
 * @param {*} url: 请求地址
 * @param {*} params: 请求参数
 */
export function downFile(url, params) {
    return new Promise((resolve, reject) => {
        axios({
            method: "get",
            url: url,
            params: params,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            responseType: 'blob',
            baseURL: BaseURL
        })
            .then((res) => {
                resolve(res)
            })
            .catch((err) => {
                reject("错误信息:", err);
            });
    })
}
    // 下载文件
    download(res,fileName) {
      console.log(res);
      let blob = new Blob([res.data], {
        type: "application/octet-stream;charset=utf-8",
      }); // res.data就是接口返回的文件流了

      if ("download" in document.createElement("a")) {
        // 非IE下载
        const elink = document.createElement("a");
        elink.download = fileName;
        elink.style.display = "none";
        elink.href = URL.createObjectURL(blob);
        document.body.appendChild(elink);
        elink.click();
        URL.revokeObjectURL(elink.href); // 释放URL 对象
        document.body.removeChild(elink);
      } else {
        // IE10+下载
        navigator.msSaveBlob(blob, fileName);
      }
    },

使用

        this.downFile("/export", {
          endDate: max,
          startDate: min,
        })
          .then((res) => {
            this.download(res, fileName);
          })
          .catch((req) => {});

附录

先前写过的上传方法

众里寻他千百度,蓦然回首,那人却在灯火阑珊处:上传の方法 - 乐盘游 - 博客园 (cnblogs.com)

关于文件上传的参考文章,不过直接用el-upload也行

文件上传,搞懂这8种场景就够了 - 掘金 (juejin.cn)

人生到处知何似,应似飞鸿踏雪泥。
原文地址:https://www.cnblogs.com/lepanyou/p/15745680.html