zoukankan      html  css  js  c++  java
  • 那么轻,那么重:图片下载与压缩包下载

    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)

    人生到处知何似,应似飞鸿踏雪泥。
  • 相关阅读:
    scala :: , +:, :+ , ::: , ++ 的区别
    Scala 函数式编程思想
    Scala 关键字
    HDFS 的内存存储是什么?
    LRU
    高并发情况限流
    Java中List集合去除重复数据的方法
    MySQL支持的跨库事务
    死磕ConcurrentHashMap 1.8源码解析
    一致性Hash算法
  • 原文地址:https://www.cnblogs.com/lepanyou/p/15745680.html
Copyright © 2011-2022 走看看