HOOKS

Public Hooks package file download useDownloadFile

File Download Wrapper for Public Hooks: useDownloadFile

Foreword

For front-end developers who frequently build enterprise management backends, working with tables to manipulate data is essential. When refactoring existing projects, it’s a good practice to encapsulate some reusable custom hooks. This article is ‘useDownloadFile.js’

Hooks encapsulated based on a personal project environment; this article only shares insights and best practices for hook encapsulation, so the accompanying code may not be directly applicable to others.

Project Environment

Vue 3.x + Ant Design Vue 3.x + Vite 3.x

For all kinds of resource files (pictures, documents, audio and video, etc.) that are common in the enterprise management background, it is normal to download and save them locally. In order to ensure uniformity and avoid repeated writing of redundant codes in each single-page file, this method is encapsulated.

Prerequisites for Encapsulation: Method Comparisons

Method Operating Principle Advantages Disadvantages
form form dynamically generate a form, and use the function of form submission to download files good compatibility, no URL length limit problem unable to know the download progress, poor user experience interaction
unable to directly download the file type that the browser can directly preview
window.open / location.href open a new tab page to access downloaded resources simple and crude there will be the problem of URL length limitation
unable to know the download progress and poor user experience interaction
unable to directly download the file type that the browser can directly preview
need to pay attention to the problem of url encoding
header cannot be added and authentication cannot be performed
’ download attribute use the native access attribute of a tag, add the newly added download attribute, and download it using a browser simple and crude, and normal preview file can be downloaded cross-domain address file cannot be downloaded
IE/Edge internal compatibility problem
authentication cannot be performed
blob object send a request to obtain binary data, convert it into blob object, generate url address by URL.createObjectUrl, assign value to href attribute of a tag, and download it in combination with download can solve the problem that files browsable by browser cannot be directly downloaded
authentication is available
IE10 is not available
Safari usage may have problems

In summary and combined with the actual project, and finally use the Blob object to package the download file method.

Encapsulation and Decomposition: Download the Core Code

xhr.onloadend = function (e) {
  if (e.target.status === 200 || e.target.status === 304) {
    const aElement = document.createElement('a');
    const blob = e.target.response;
    const url = window.URL.createObjectURL(blob);
    aElement.style.display = 'none';
    aElement.href = url;
    aElement.download = `${options.fileName}.${fileType}`;
    document.body.appendChild(aElement);
    aElement.click();
    if (window.URL) {
      window.URL.revokeObjectURL(url);
    } else {
      window.webkitURL.revokeObjectURL(url);
    }
    document.body.removeChild(aElement);
  }
};
xhr.send();

Component Decomposition: User Experience Design

-During the download process, the Ant Design Vue framework used in conjunction with the project can enhance the user’s perception of file download progress, -To prevent users from repeatedly triggering downloads through forceful clicks, a loading flag is used as an identifier. -After a download failure, prompt the user to retry the download.

createVNode('div', {}, ['文件下载过程中请勿关闭当前页面']),
createVNode('div', { className: 'mt-2' }, [`当前下载进度 ${progress.value}%`]),

catch (e) {
  console.error(e);
  downloading = false;
  infoModal && infoModal.destroy();
  Modal.error({
    title: '提示',
    content: '下载发生异常,请重试',
  });
}

Complete code for useDownloadFile.js


export function useDownloadFile() {
  let xhr = null;
  let downloading = false; // 限制同一文件同时触发多次下载
  let infoModal;

  onBeforeUnmount(() => {
    xhr && xhr.abort();
  });

  const downloadFile = options => {
    try {
      if (downloading || !options.url || !options.fileName) return;
      downloading = true;
      options.url = options.url.replace('http://', 'https://');
      const progress = ref(0);
      const fileType = options.url.split('.').pop();
      xhr = new XMLHttpRequest();
      xhr.responseType = 'blob';
      xhr.open('get', options.url, true);
      infoModal = Modal.info({
        title: '文件下载',
        okText: '取消下载',
        content: () => {
          return createVNode('div', {}, [
            createVNode('div', {}, ['文件下载过程中请勿关闭当前页面']),
            createVNode('div', { className: 'mt-2' }, [`当前下载进度 ${progress.value}%`]),
          ]);
        },
        onOk() {
          xhr.abort();
          return Promise.resolve();
        },
      });
      xhr.onprogress = function (e) {
        progress.value = Math.floor((e.loaded / e.total) * 100);
        if (progress.value === 100) {
          downloading = false;
          infoModal.destroy();
        }
      };
      xhr.onloadend = function (e) {
        if (e.target.status === 200 || e.target.status === 304) {
          const aElement = document.createElement('a');
          const blob = e.target.response;
          const url = window.URL.createObjectURL(blob);
          aElement.style.display = 'none';
          aElement.href = url;
          aElement.download = `${options.fileName}.${fileType}`;
          document.body.appendChild(aElement);
          aElement.click();
          if (window.URL) {
            window.URL.revokeObjectURL(url);
          } else {
            window.webkitURL.revokeObjectURL(url);
          }
          document.body.removeChild(aElement);
        }
      };
      xhr.send();
    } catch (e) {
      console.error(e);
      downloading = false;
      infoModal && infoModal.destroy();
      Modal.error({
        title: '提示',
        content: '下载发生异常,请重试',
      });
    }
  };

  return {
    downloadFile,
  };
}

Related posts

View all →