最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

一、需求場景

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

二、實(shí)現(xiàn)原理

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

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

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

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

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

將靜態(tài)資源文件放到public文件夾下,這樣啟動(dòng)項(xiàng)目后,可直接通過http://localhost:3000/1.pdf 的方式訪問到靜態(tài)資源。在實(shí)際工作中,肯定是直接訪問服務(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; //下載進(jìn)度百分比
  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); //默認(rèn)異步請求
    xhr.responseType = 'blob';
    xhr.onprogress = (e) => {
      //判斷資源長度是否可計(jì)算
      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標(biāo)簽將資源下載
        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ā)送時(shí)將進(jìn)度重置為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: '報(bào)告.doc',
    url: 'http://localhost:3000/報(bào)告.doc',
    type: 'doc',
  },
];
interface Options {
  url: string;
  fileName: string;
}

const Item = memo(({ url, fileName }: Options) => {
  //每項(xiàng)都需擁有一個(gè)屬于自己的 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 實(shí)現(xiàn)步驟

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

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

將靜態(tài)資源文件放到public文件夾下,這樣啟動(dòng)項(xiàng)目后,可直接通過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; //下載進(jìn)度百分比
  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); //默認(rèn)異步請求
    xhr.responseType = "blob";
    xhr.onprogress = (e) => {
      //判斷資源長度是否可計(jì)算
      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標(biāo)簽將資源下載
        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ā)送時(shí)將進(jìn)度重置為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: "報(bào)告.doc",
    url: "http://127.0.0.1:5173/報(bào)告.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ù)器默認(rèn)開啟文件分塊編碼(響應(yīng)頭返回Transfer-Encoding: chunked)。分塊編碼把「報(bào)文」分割成若干個(gè)大小已知的塊,塊之間是緊挨著發(fā)送的。采用這種傳輸方式進(jìn)行響應(yīng)時(shí),不會(huì)傳Content-Length這個(gè)首部信息,即使帶上了也是不準(zhǔn)確的

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

clipboard.png

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

7ACD3DB2BB1B4EF9B8CB59505F92C49E.jpg

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

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

相關(guān)文章

  • 關(guān)于react中列表渲染的局部刷新問題

    關(guān)于react中列表渲染的局部刷新問題

    這篇文章主要介紹了關(guān)于react中列表渲染的局部刷新問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • Redux中subscribe的作用及說明

    Redux中subscribe的作用及說明

    由于redux使用這方面有很多的不解,不是很熟練,所以我查找資料,進(jìn)行一個(gè)總結(jié),希望可以鞏固知識(shí),并且能幫助到需要的人,所以我會(huì)寫的比較清晰簡單明了點(diǎn),若有不對之處,請大家糾正
    2023-10-10
  • 手挽手帶你學(xué)React之React-router4.x的使用

    手挽手帶你學(xué)React之React-router4.x的使用

    這篇文章主要介紹了手挽手帶你學(xué)React之React-router4.x的使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • 深入理解React中Suspense與lazy的原理

    深入理解React中Suspense與lazy的原理

    在react中為我們提供了一個(gè)非常有用的組件,那就是Suspense,本文主要介紹了如何使用Suspense?和?react提供的lazy結(jié)合起來達(dá)到異步加載狀態(tài)的目的,感興趣的可以了解下
    2024-04-04
  • react redux中如何獲取store數(shù)據(jù)并將數(shù)據(jù)渲染出來

    react redux中如何獲取store數(shù)據(jù)并將數(shù)據(jù)渲染出來

    這篇文章主要介紹了react redux中如何獲取store數(shù)據(jù)并將數(shù)據(jù)渲染出來,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • React?狀態(tài)管理工具優(yōu)劣勢示例分析

    React?狀態(tài)管理工具優(yōu)劣勢示例分析

    這篇文章主要為大家介紹了React?狀態(tài)管理工具優(yōu)劣勢示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • react 可拖拽進(jìn)度條的實(shí)現(xiàn)

    react 可拖拽進(jìn)度條的實(shí)現(xiàn)

    本文主要介紹了react 可拖拽進(jìn)度條的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • React?Context詳解使用方法

    React?Context詳解使用方法

    Context提供了一個(gè)無需為每層組件手動(dòng)添加props,就能在組件樹間進(jìn)行數(shù)據(jù)傳遞的方法。在一個(gè)典型的?React?應(yīng)用中,數(shù)據(jù)是通過props屬性自上而下(由父及子)進(jìn)行傳遞的,但這種做法對于某些類型的屬性而言是極其繁瑣的
    2022-12-12
  • React中Key屬性作用

    React中Key屬性作用

    react中的key屬性,它是一個(gè)特殊的屬性,它是出現(xiàn)不是給開發(fā)者用的,而是給React自己使用,有了key屬性后,就可以與組件建立了一種對應(yīng)關(guān)系,本文主要介紹了React中Key屬性作用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • React Router6.x路由表封裝的兩種寫法

    React Router6.x路由表封裝的兩種寫法

    本文主要介紹了React Router6.x路由表封裝的兩種寫法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01

最新評論

苏尼特左旗| 太原市| 嘉黎县| 安化县| 盈江县| 固阳县| 江安县| 大洼县| 象州县| 永靖县| 开封市| 鄄城县| 麻栗坡县| 上栗县| 崇州市| 丹东市| 雷山县| 开原市| 上虞市| 遵义县| 孙吴县| 武穴市| 宁安市| 射阳县| 玉环县| 富锦市| 高淳县| 茌平县| 汨罗市| 涟源市| 会昌县| 万盛区| 东光县| 泰来县| 池州市| 额济纳旗| 临澧县| 潍坊市| 准格尔旗| 顺平县| 巴楚县|