亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

React和Vue實現(xiàn)文件下載進度條

 更新時間:2023年04月26日 16:01:17   作者:敲代碼的彭于晏  
本文主要介紹了React和Vue實現(xiàn)文件下載進度條,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

一、需求場景

下載服務(wù)端大文件資源過慢,頁面沒有任何顯示,體驗太差。因此需增加進度條優(yōu)化顯示

二、實現(xiàn)原理

  • 發(fā)送異步HTTP請求,監(jiān)聽onprogress事件,讀取已下載的資源和資源總大小得到下載百分比

  • 在資源請求完成后,將文件內(nèi)容轉(zhuǎn)為blob,并通過a標簽將文件通過瀏覽器下載下來

三、react 實現(xiàn)步驟

1. 托管靜態(tài)資源

前提:通過create-react-app創(chuàng)建的react項目

將靜態(tài)資源文件放到public文件夾下,這樣啟動項目后,可直接通過http://localhost:3000/1.pdf 的方式訪問到靜態(tài)資源。在實際工作中,肯定是直接訪問服務(wù)器上的資源

2. 封裝hook

新建useDownload.ts

import { useCallback, useRef, useState } from 'react';

interface Options {
  fileName: string; //下載的文件名
  onCompleted?: () => void; //請求完成的回調(diào)方法
  onError?: (error: Error) => void; //請求失敗的回調(diào)方法
}

interface FileDownReturn {
  download: () => void; //下載
  cancel: () => void; //取消
  progress: number; //下載進度百分比
  isDownloading: boolean; //是否下載中
}

export default function useFileDown(url: string, options: Options): FileDownReturn {
  const { fileName, onCompleted, onError } = options;
  const [progress, setProgress] = useState(0);
  const [isDownloading, setIsDownloading] = useState(false);
  const xhrRef = useRef<XMLHttpRequest | null>(null);

  const download = useCallback(() => {
    const xhr = (xhrRef.current = new XMLHttpRequest());
    xhr.open('GET', url); //默認異步請求
    xhr.responseType = 'blob';
    xhr.onprogress = (e) => {
      //判斷資源長度是否可計算
      if (e.lengthComputable) {
        const percent = Math.floor((e.loaded / e.total) * 100);
        setProgress(percent);
      }
    };
    xhr.onload = () => {
      if (xhr.status === 200) {
        //請求資源完成,將文件內(nèi)容轉(zhuǎn)為blob
        const blob = new Blob([xhr.response], { type: 'application/octet-stream' });
        //通過a標簽將資源下載
        const link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = decodeURIComponent(fileName);
        link.click();
        window.URL.revokeObjectURL(link.href);
        onCompleted && onCompleted();
      } else {
        onError && onError(new Error('下載失敗'));
      }
      setIsDownloading(false);
    };
    xhr.onerror = () => {
      onError && onError(new Error('下載失敗'));
      setIsDownloading(false);
    };
    xhrRef.current.send(); //發(fā)送請求
    setProgress(0); //每次發(fā)送時將進度重置為0
    setIsDownloading(true);
  }, [fileName, onCompleted, onError, url]);

  const cancel = useCallback(() => {
    xhrRef.current?.abort(); //取消請求
    setIsDownloading(false);
  }, [xhrRef]);

  return {
    download,
    cancel,
    progress,
    isDownloading,
  };
}

3. 使用hook

import { memo } from 'react';

import useFileDown from './useDownload';

const list = [
  {
    fileName: '城市發(fā)展史起.pdf',
    url: ' http://localhost:3000/1.pdf',
    type: 'pdf',
  },
  {
    fileName: '表格.xlsx',
    url: 'http://localhost:3000/表格.xlsx',
    type: 'xlsx',
  },
  {
    fileName: '報告.doc',
    url: 'http://localhost:3000/報告.doc',
    type: 'doc',
  },
];
interface Options {
  url: string;
  fileName: string;
}

const Item = memo(({ url, fileName }: Options) => {
  //每項都需擁有一個屬于自己的 useFileDown hook
  const { download, cancel, progress, isDownloading } = useFileDown(url, { fileName });

  return (
    <div>
      <span style={{ cursor: 'pointer' }} onClick={download}>
        {fileName}
      </span>
      {isDownloading ? (
        <span>
          {`下載中:${progress}`}
          <button onClick={cancel}>取消下載</button>
        </span>
      ) : (
        ''
      )}
    </div>
  );
});

const Download = () => {
  return (
    <div>
      {list.map((item, index) => (
        <Item url={item.url} fileName={item.fileName} key={index} />
      ))}
    </div>
  );
};

export default Download;

四、vue 實現(xiàn)步驟

1. 托管靜態(tài)資源

前提:通過vite創(chuàng)建的vue項目

將靜態(tài)資源文件放到public文件夾下,這樣啟動項目后,可直接通過http://127.0.0.1:5173/1.pdf 的方式訪問到靜態(tài)資源

2. 封裝hook

新建hooks/useDownload.ts(新建hooks文件夾)

import { ref } from "vue";

export interface Options {
  fileName: string;
  onCompleted?: () => void; //請求完成的回調(diào)方法
  onError?: (error: Error) => void; //請求失敗的回調(diào)方法
}

export interface FileDownReturn {
  download: () => void; //下載
  cancel: () => void; //取消
  progress: number; //下載進度百分比
  isDownloading: boolean; //是否下載中
}

export default function useFileDown(
  url: string,
  options: Options
): FileDownReturn {
  const { fileName, onCompleted, onError } = options;
  const progress = ref(0);
  const isDownloading = ref(false);

  const xhrRef = ref<XMLHttpRequest | null>(null);

  const download = () => {
    const xhr = (xhrRef.value = new XMLHttpRequest());
    xhr.open("GET", url); //默認異步請求
    xhr.responseType = "blob";
    xhr.onprogress = (e) => {
      //判斷資源長度是否可計算
      if (e.lengthComputable) {
        const percent = Math.floor((e.loaded / e.total) * 100);
        progress.value = percent;
      }
    };
    xhr.onload = () => {
      if (xhr.status === 200) {
        //請求資源完成,將文件內(nèi)容轉(zhuǎn)為blob
        const blob = new Blob([xhr.response], {
          type: "application/octet-stream",
        });
        //通過a標簽將資源下載
        const link = document.createElement("a");
        link.href = window.URL.createObjectURL(blob);
        link.download = decodeURIComponent(fileName);
        link.click();
        window.URL.revokeObjectURL(link.href);
        onCompleted && onCompleted();
      } else {
        onError && onError(new Error("下載失敗"));
      }
      isDownloading.value = false;
    };
    xhr.onerror = () => {
      onError && onError(new Error("下載失敗"));
      isDownloading.value = false;
    };
    xhrRef.value.send(); //發(fā)送請求
    progress.value = 0; //每次發(fā)送時將進度重置為0
    isDownloading.value = true;
  };

  const cancel = () => {
    xhrRef.value?.abort(); //取消請求
    isDownloading.value = false;
  };

  return {
    download,
    cancel,
    progress,
    isDownloading,
  };
}

3. 使用hook

  • 修改App.vue
<script setup lang="ts">
import Item from "./components/Item.vue";

const list = [
  {
    fileName: "城市發(fā)展史起.pdf",
    url: " http://127.0.0.1:5173/1.pdf",
    type: "pdf",
  },
  {
    fileName: "表格.xlsx",
    url: "http://127.0.0.1:5173/表格.xlsx",
    type: "xlsx",
  },
  {
    fileName: "報告.doc",
    url: "http://127.0.0.1:5173/報告.doc",
    type: "doc",
  },
];
</script>

<template>
  <div>
    <div v-for="(item, index) in list" :key="index">
      <Item :url="item.url" :fileName="item.fileName"<script setup lang="ts">
import useFileDown from "../hooks/useDownload.ts";


const props = defineProps<{ url: string; fileName: string }>();

const { url, fileName } = props;

const { download, cancel, progress, isDownloading } = useFileDown(url, {
  fileName,
});
</script>

<template>
  <div>
    <span style="cursor: pointer" @click="download">
      {{ fileName }}
    </span>
    <span v-if="isDownloading">
      下載中:{{ progress }} <button @click="cancel">取消下載</button></span
    >
  </div>
</template> />
    </div>
  </div>
</template>
  • 新建components/Item.vue
<script setup lang="ts">
import useFileDown from "../hooks/useDownload.ts";


const props = defineProps<{ url: string; fileName: string }>();

const { url, fileName } = props;

const { download, cancel, progress, isDownloading } = useFileDown(url, {
  fileName,
});
</script>

<template>
  <div>
    <span style="cursor: pointer" @click="download">
      {{ fileName }}
    </span>
    <span v-if="isDownloading">
      下載中:{{ progress }} <button @click="cancel">取消下載</button></span
    >
  </div>
</template>

五、可能遇到的問題:lengthComputable為false

原因一:后端響應(yīng)頭沒有返回Content-Length;

解決辦法:讓后端加上就行

原因二:開啟了gzip壓縮

開啟gzip之后服務(wù)器默認開啟文件分塊編碼(響應(yīng)頭返回Transfer-Encoding: chunked)。分塊編碼把「報文」分割成若干個大小已知的塊,塊之間是緊挨著發(fā)送的。采用這種傳輸方式進行響應(yīng)時,不會傳Content-Length這個首部信息,即使帶上了也是不準確的

分別為gzip壓縮,分塊編碼:

clipboard.png

例如有個877k大小的js文件,網(wǎng)絡(luò)請求的大小為247k。但是打印的e.loaded最終返回的是877k

7ACD3DB2BB1B4EF9B8CB59505F92C49E.jpg

解決方法:后端把文件大小存儲到其他字段,比如:header['x-content-length']

到此這篇關(guān)于React和Vue實現(xiàn)文件下載進度條的文章就介紹到這了,更多相關(guān)React Vue下載進度條內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • react?中?mobx的使用案例詳解

    react?中?mobx的使用案例詳解

    這篇文章主要介紹了react?中?mobx的使用案例詳解,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • React Native react-navigation 導(dǎo)航使用詳解

    React Native react-navigation 導(dǎo)航使用詳解

    本篇文章主要介紹了React Native react-navigation 導(dǎo)航使用詳解,詳解的介紹了react-navigation導(dǎo)航的使用,具有一定的參考價值,有興趣的可以了解一下
    2017-12-12
  • create-react-app如何降低react的版本

    create-react-app如何降低react的版本

    這篇文章主要介紹了create-react-app降低react的版本方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • React渲染機制及相關(guān)優(yōu)化方案

    React渲染機制及相關(guān)優(yōu)化方案

    這篇文章主要介紹了react中的渲染機制以及相關(guān)的優(yōu)化方案,內(nèi)容包括react渲染步驟、concurrent機制以及產(chǎn)生作用的機會,簡單模擬實現(xiàn) concurrent mode,基于作業(yè)調(diào)度優(yōu)先級的思路進行項目優(yōu)化的兩個hooks,感興趣的小伙伴跟著小編一起來看看吧
    2023-07-07
  • ReactNative實現(xiàn)圖片上傳功能的示例代碼

    ReactNative實現(xiàn)圖片上傳功能的示例代碼

    本篇文章主要介紹了ReactNative實現(xiàn)圖片上傳功能的示例代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下。
    2017-07-07
  • react map使用方法實例詳解

    react map使用方法實例詳解

    map()方法是在React中常用的數(shù)組處理方法之一,可以用于遍歷數(shù)組、生成組件列表以及進行數(shù)據(jù)轉(zhuǎn)換等操作,通過合理運用map()方法,可以更靈活地處理和展示數(shù)據(jù),下面給大家講解react map使用方法,感興趣的朋友一起看看吧
    2023-10-10
  • 一篇文章帶你理解React Props的 原理

    一篇文章帶你理解React Props的 原理

    這篇文章主要為大家介紹了ReactProps的原理,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • 淺談redux以及react-redux簡單實現(xiàn)

    淺談redux以及react-redux簡單實現(xiàn)

    這篇文章主要介紹了淺談redux以及react-redux簡單實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • react-dnd實現(xiàn)任意拖動與互換位置

    react-dnd實現(xiàn)任意拖動與互換位置

    這篇文章主要為大家詳細介紹了react-dnd實現(xiàn)任意拖動與互換位置,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 詳解如何在React函數(shù)式組件中使用MobX

    詳解如何在React函數(shù)式組件中使用MobX

    MobX 是一個簡潔的狀態(tài)管理庫,它通過透明的函數(shù)響應(yīng)式編程(TFRP)使得狀態(tài)管理變得簡單和可擴展,下面就跟隨小編一起來了解一下如何在React函數(shù)式組件中使用MobX吧
    2024-01-01

最新評論