前端封装的一个下载文件的函数

780 阅读7分钟

前端封装的一个下载文件的函数

export const downloadFile = (fileInfo?: any) => {
  const { url, params } = fileInfo;
  return new Promise((reslove, reject) => {
    axios({
      method: "get",
      url: url,
      responseType: "blob", //服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream',默认是'json'
      timeout: 0, // 默认值是 `0` (永不超时)
      params: params
    })
      .then(({ data, headers }) => {
        let fileName = url.substr(url.lastIndexOf("/") + 1);
        const contentDisposition = headers["content-disposition"];
        if (contentDisposition) {
          const name1 = contentDisposition.split(";")[2];
          if (name1) {
            const name2 = name1.split("filename*=")[1];
            if (name2) {
              const name3 = name2.split("''")[1];
              if (name3) {
                const name4 = name3.split(".")[0];
                if (name4) {
                  fileName = decodeURI(name4);
                }
              }
            }
          }
        }
        if (!data) {
          reslove("");
          return;
        }
        let blobType = "";
        const contentType = headers["content-type"];
        if (contentType) {
          blobType = contentType;
        }
        const blob = new Blob([data], {
          type: blobType
        });
        // if (window.navigator.msSaveOrOpenBlob) {
        //   //兼容IE10
        //   navigator.msSaveBlob(blob, fileName);
        // } else {
        const URL = window.URL || window.webkitURL;
        const href = URL.createObjectURL(blob); //创建新的URL表示指定的blob对象
        const a = document.createElement("a"); //创建a标签
        a.style.display = "none";
        a.href = href; // 指定下载链接
        a.download = fileName; //指定下载文件名
        document.body.appendChild(a);
        a.click(); //触发下载
        URL.revokeObjectURL(a.href); //释放URL对象
        document.body.removeChild(a); //下载完成移除
        // }
        // 这里也可以不创建a链接,直接window.open(href)也能下载
        reslove("");
      })
      .catch(err => {
        console.log("downloadFile err", err);
        reject();
      });
  });
};

逐步解释

axios({
      method: "get",
      url: url,
      responseType: "blob", //服务器响应的数据类型,可以是 'arraybuffer', 'blob', 'document', 'json', 'text', 'stream',默认是'json'
      timeout: 0, // 默认值是 `0` (永不超时)
      params: params
    })

responseType

这是发送axios请求,url是要下载的文件地址,params是其他的自定义参数

设置responseType为blob,在 Axios 中,responseType 用于指定服务器响应的数据类型。以下是 responseType 的可选值及其含义:

1. arraybuffer

  • 描述:表示响应数据将作为 ArrayBuffer 返回。

  • 用途:通常用于处理二进制数据,例如图像、音频、视频文件等。

2. blob

  • 描述:表示响应数据将作为 Blob 返回。

  • 用途:通常用于处理文件下载。

3. document

  • 描述:表示响应数据将作为 Document 对象返回。

  • 用途:用于处理 HTML/XML 文档。

4. json

  • 描述:表示响应数据将作为 JSON 对象返回。默认值。

  • 用途:用于处理 JSON 格式的数据。

5. text

  • 描述:表示响应数据将作为 string 返回。

  • 用途:用于处理文本数据。

6. stream

  • 描述:表示响应数据将作为 stream 返回(仅在 Node.js 环境中可用)。

  • 用途:用于处理流数据,通常用于文件下载或大数据量传输。

.then(({ data, headers }) => {
        let fileName = url.substr(url.lastIndexOf("/") + 1);
        const contentDisposition = headers["content-disposition"];
        if (contentDisposition) {
          const name1 = contentDisposition.split(";")[2];
          if (name1) {
            const name2 = name1.split("filename*=")[1];
            if (name2) {
              const name3 = name2.split("''")[1];
              if (name3) {
                const name4 = name3.split(".")[0];
                if (name4) {
                  fileName = decodeURI(name4);
                }
              }
            }
          }
        }
        if (!data) {
          reslove("");
          return;
        }
        let blobType = "";
        const contentType = headers["content-type"];
        if (contentType) {
          blobType = contentType;
        }
        const blob = new Blob([data], {
          type: blobType
        });
        // if (window.navigator.msSaveOrOpenBlob) {
        //   //兼容IE10
        //   navigator.msSaveBlob(blob, fileName);
        // } else {
        const URL = window.URL || window.webkitURL;
        const href = URL.createObjectURL(blob); //创建新的URL表示指定的blob对象
        const a = document.createElement("a"); //创建a标签
        a.style.display = "none";
        a.href = href; // 指定下载链接
        a.download = fileName; //指定下载文件名
        document.body.appendChild(a);
        a.click(); //触发下载
        URL.revokeObjectURL(a.href); //释放URL对象
        document.body.removeChild(a); //下载完成移除
        // }
        // 这里也可以不创建a链接,直接window.open(href)也能下载
        reslove("");
      })
      .catch(err => {
        console.log("downloadFile err", err);
        reject();
      });

then和catch分别处理axios成功和失败的结果

then

{data,header}是从axios返回的结果res身上解构的

let fileName = url.substr(url.lastIndexOf("/") + 1);
        const contentDisposition = headers["content-disposition"];
        if (contentDisposition) {
          const name1 = contentDisposition.split(";")[2];
          if (name1) {
            const name2 = name1.split("filename*=")[1];
            if (name2) {
              const name3 = name2.split("''")[1];
              if (name3) {
                const name4 = name3.split(".")[0];
                if (name4) {
                  fileName = decodeURI(name4);
                }
              }
            }
          }
        }

fileName是从url参数上获取到的文件名,url.substr(url.lastIndexOf("/") + 1)是截取最后一个/到最后的字符串, 假设 url 的值是 https://example.com/files/document.pdf

  1. url.lastIndexOf("/"):找到 URL 中最后一个斜杠 / 的位置。在这个例子中,它会返回 24,因为最后一个斜杠的位置是第 24 个字符。
  2. url.substr(url.lastIndexOf("/") + 1):从 url 中提取从第 25 个字符(24 + 1)开始的所有字符,这里就是 document.pdf

const contentDisposition = headers["content-disposition"]; 这一行代码用于从响应头中获取 Content-Disposition 字段的值。Content-Disposition 头通常用于指示如何处理响应内容,尤其是在文件下载的场景中。它可以包含文件名信息,帮助客户端正确命名下载的文件。

详细解释

当服务器发送文件下载响应时,响应头中可能包含 Content-Disposition 字段,如下所示:

css
复制代码
Content-Disposition: attachment; filename="example.txt"

这个字段指示浏览器以附件的形式处理响应内容,并将文件名设为 example.txt。在 Axios 响应对象中,响应头可以通过 headers 属性访问。

if (contentDisposition) {
          const name1 = contentDisposition.split(";")[2];
          if (name1) {
            const name2 = name1.split("filename*=")[1];
            if (name2) {
              const name3 = name2.split("''")[1];
              if (name3) {
                const name4 = name3.split(".")[0];
                if (name4) {
                  fileName = decodeURI(name4);
                }
              }
            }
          }
        }

这段代码用于从 Content-Disposition 响应头中解析出文件名。以下是详细的解析步骤:

  1. 检查 Content-Disposition

    javascript
    复制代码
    if (contentDisposition) {
    

    首先,检查响应头中是否存在 Content-Disposition 字段。

  2. 分割 Content-Disposition

    javascript
    复制代码
    const name1 = contentDisposition.split(";")[2];
    

    使用分号 ; 分割 Content-Disposition 字段,并取第三个部分(索引为2),因为文件名通常在第三部分。Content-Disposition 字段通常类似于 attachment; filename="example.txt"attachment; filename*=UTF-8''example.txt

  3. 检查并分割 filename*=

    javascript
    复制代码
    if (name1) {
      const name2 = name1.split("filename*=")[1];
    

    检查第三部分是否存在,并尝试使用 filename*= 分割。filename*= 通常用于指定编码格式和文件名。

  4. 分割编码和文件名

    javascript
    复制代码
    if (name2) {
      const name3 = name2.split("''")[1];
    

    如果 filename*= 存在,再使用 '' 分割,取第二部分(索引为1),这部分通常是文件名。

  5. 分割文件名和扩展名

    javascript
    复制代码
    if (name3) {
      const name4 = name3.split(".")[0];
    

    如果文件名存在,再使用点 . 分割,取第一部分(索引为0),即文件名(不含扩展名)。

  6. 解码文件名

    javascript
    复制代码
    if (name4) {
      fileName = decodeURI(name4);
    }
    

    最后,如果文件名存在,使用 decodeURI 函数对其进行解码。

示例 Content-Disposition 响应头

假设 Content-Disposition 头为:

css
复制代码
Content-Disposition: attachment; filename*=UTF-8''example.txt
  1. 分割后得到 ["attachment", " filename*=UTF-8''example.txt"]
  2. 取第三部分(实际上是第二部分,因为数组索引从0开始)。
  3. 进一步分割 filename*=,得到 ["UTF-8", "example.txt"]
  4. 再分割 '',得到 ["UTF-8", "example.txt"]
  5. 使用 . 分割 example.txt,得到 ["example", "txt"]
  6. 最终文件名为 example
 if (!data) {
          reslove("");
          return;
        }
        let blobType = "";
        const contentType = headers["content-type"];
        if (contentType) {
          blobType = contentType;
        }
        const blob = new Blob([data], {
          type: blobType
        });

这段代码的功能是处理从服务器获取的响应数据,并根据响应头中的 Content-Type 创建一个 Blob 对象。以下是对这段代码的逐行解释:

  1. 检查数据是否为空

    javascript
    复制代码
    if (!data) {
      reslove("");
      return;
    }
    
    • 如果 data 为空,调用 resolve 结束 Promise 并返回空字符串。
  2. 初始化 blobType

    javascript
    复制代码
    let blobType = "";
    
    • 初始化 blobType 为一个空字符串。blobType 用于存储响应数据的 MIME 类型。
  3. 检查 Content-Type 响应头

    javascript
    复制代码
    const contentType = headers["content-type"];
    if (contentType) {
      blobType = contentType;
    }
    
    • 获取响应头中的 Content-Type 字段。如果存在,将其赋值给 blobType
  4. 创建 Blob 对象

    javascript
    复制代码
    const blob = new Blob([data], {
      type: blobType
    });
    
    • 使用响应数据创建一个新的 Blob 对象,并指定其类型为 blobType
const URL = window.URL || window.webkitURL;
        const href = URL.createObjectURL(blob); //创建新的URL表示指定的blob对象
        const a = document.createElement("a"); //创建a标签
        a.style.display = "none";
        a.href = href; // 指定下载链接
        a.download = fileName; //指定下载文件名
        document.body.appendChild(a);
        a.click(); //触发下载
        URL.revokeObjectURL(a.href); //释放URL对象
        document.body.removeChild(a); //下载完成移除
        // }
        // 这里也可以不创建a链接,直接window.open(href)也能下载
        reslove("");

这段代码用于在浏览器中创建一个用于下载文件的链接,并自动触发下载。以下是逐行解释:

  1. 获取 URL 创建函数

    javascript
    复制代码
    const URL = window.URL || window.webkitURL;
    
    • 获取 URL 创建函数,兼容不同浏览器。如果浏览器支持 window.URL,则使用 window.URL,否则使用 window.webkitURL
  2. 创建 Blob 对象的 URL

    javascript
    复制代码
    const href = URL.createObjectURL(blob);
    
    • 使用 createObjectURL 方法为 blob 对象创建一个临时的 URL。这个 URL 可以用来访问 blob 的内容。
  3. 创建 <a> 标签

    javascript
    复制代码
    const a = document.createElement("a");
    
    • 创建一个新的 <a> 标签元素。
  4. 隐藏 <a> 标签

    javascript
    复制代码
    a.style.display = "none";
    
    • <a> 标签隐藏(设置 display: none),以免影响页面布局。
  5. 设置下载链接

    javascript
    复制代码
    a.href = href;
    
    • <a> 标签的 href 属性设置为上一步创建的 blob URL。
  6. 设置下载文件名

    javascript
    复制代码
    a.download = fileName;
    
    • <a> 标签的 download 属性设置为下载文件的文件名。
  7. 添加 <a> 标签到文档中

    javascript
    复制代码
    document.body.appendChild(a);
    
    • <a> 标签添加到文档的 body 中,以使其成为 DOM 的一部分。
  8. 触发下载

    javascript
    复制代码
    a.click();
    
    • 程序化地触发 <a> 标签的 click 事件,开始下载文件。
  9. 释放 URL 对象

    javascript
    复制代码
    URL.revokeObjectURL(a.href);
    
    • 调用 revokeObjectURL 方法释放之前创建的 blob URL,释放内存。
  10. 移除 <a> 标签

    javascript
    复制代码
    document.body.removeChild(a);
    
    • 下载完成后,将 <a> 标签从文档中移除,清理 DOM。
  11. 完成 Promise

    javascript
    复制代码
    resolve("");
    
    • 调用 resolve,标记 Promise 已完成,并返回空字符串。