91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

React和Vue怎么實現文件下載進度條

發布時間:2023-04-26 17:17:24 來源:億速云 閱讀:137 作者:iii 欄目:開發技術

這篇文章主要介紹“React和Vue怎么實現文件下載進度條”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“React和Vue怎么實現文件下載進度條”文章能幫助大家解決問題。

一、需求場景

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

二、實現原理

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

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

三、react 實現步驟

1. 托管靜態資源

前提:通過create-react-app創建的react項目

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

2. 封裝hook

新建useDownload.ts

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

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

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) {
        //請求資源完成,將文件內容轉為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(); //發送請求
    setProgress(0); //每次發送時將進度重置為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: '城市發展史起.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 實現步驟

1. 托管靜態資源

前提:通過vite創建的vue項目

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

2. 封裝hook

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

import { ref } from "vue";

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

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) {
        //請求資源完成,將文件內容轉為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(); //發送請求
    progress.value = 0; //每次發送時將進度重置為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: "城市發展史起.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  @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  @click="download">
      {{ fileName }}
    </span>
    <span v-if="isDownloading">
      下載中:{{ progress }} <button @click="cancel">取消下載</button></span
    >
  </div>
</template>

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

原因一:后端響應頭沒有返回Content-Length;

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

原因二:開啟了gzip壓縮

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

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

React和Vue怎么實現文件下載進度條

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

React和Vue怎么實現文件下載進度條

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

關于“React和Vue怎么實現文件下載進度條”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

民勤县| 彰武县| 枣阳市| 旌德县| 横山县| 东至县| 洪雅县| 八宿县| 安仁县| 旌德县| 班玛县| 宜宾县| 海伦市| 金平| 南部县| 辽宁省| 东乡族自治县| 澄江县| 开平市| 博客| 平原县| 富蕴县| 绩溪县| 临沧市| 穆棱市| 平塘县| 盐津县| 贵定县| 潼关县| 金昌市| 江源县| 宁安市| 山阳县| 海原县| 福贡县| 五华县| 盐亭县| 台前县| 开原市| 锦屏县| 讷河市|