我的编程空间,编程开发者的网络收藏夹
学习永远不晚

React和Vue实现文件下载进度条

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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 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

原因一:后端响应头没有返回Content-Length;

解决办法:让后端加上就行

原因二:开启了gzip压缩

开启gzip之后服务器默认开启文件分块编码(响应头返回Transfer-Encoding: chunked)。分块编码把「报文」分割成若干个大小已知的块,块之间是紧挨着发送的。采用这种传输方式进行响应时,不会传Content-Length这个首部信息,即使带上了也是不准确的

分别为gzip压缩,分块编码:

clipboard.png

例如有个877k大小的js文件,网络请求的大小为247k。但是打印的e.loaded最终返回的是877k

7ACD3DB2BB1B4EF9B8CB59505F92C49E.jpg

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

到此这篇关于React和Vue实现文件下载进度条的文章就介绍到这了,更多相关React Vue下载进度条内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

React和Vue实现文件下载进度条

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

React和Vue实现文件下载进度条

本文主要介绍了React和Vue实现文件下载进度条,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-05-18

python下载文件带进度条怎么实现

要实现带进度条的文件下载,可以使用`urllib.request`模块和`tqdm`库。以下是一个示例代码:```pythonimport urllib.requestfrom tqdm import tqdm# 下载文件的URLurl =
2023-09-26

Android文件下载进度条的实现代码

main.xml: 代码如下:2022-06-06

android中实现OkHttp下载文件并带进度条

OkHttp是比较火的网络框架,它支持同步与异步请求,支持缓存,可以拦截,更方便下载大文件与上传文件的操作。下面我们用OkHttp来下载文件并带进度条!相关资料: 官网地址:http://square.github.io/okhttp/gi
2023-05-30

php怎么实现下载进度条

本篇内容主要讲解“php怎么实现下载进度条”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“php怎么实现下载进度条”吧!php实现下载进度条的方法:1、创建“download.php”文件,代码如
2023-06-25

php如何实现下载进度条

php实现下载进度条的方法:1、创建“download.php”文件,代码如“switch ($action) {case 'prepare-download'...}”;2、通过创建js代码显示进度条即可。
2022-03-06

vue如何实现实时上传文件进度条

这篇文章主要介绍了vue如何实现实时上传文件进度条,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。具体内容如下//上传文件组件
2023-06-29

SpringBoot实现文件上传下载实时进度条功能(附源码)

这篇文章主要为大家详细介绍了SpringBoot如何实现文件上传下载实时进度条功能,文中的示例代码讲解详细,感兴趣的小伙伴可以学习一下
2022-11-13

大文件下载以及进度条展示和MD5校验

使用socket网络,上传一个视频,大小在3G左右能够显示进度条,显示花费时间下载使用TCP协议server向client发送文件新建文件server.py,代码如下:importosimportjsonimportsocketimport
2023-01-30

怎么用Python展示文件下载进度条

这篇文章主要介绍“怎么用Python展示文件下载进度条”,在日常操作中,相信很多人在怎么用Python展示文件下载进度条问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用Python展示文件下载进度条”的疑
2023-06-25

Android实现文件下载进度显示功能

和大家一起分享一下学习经验,如何实现Android文件下载进度显示功能,希望对广大初学者有帮助。 先上效果图:上方的蓝色进度条,会根据文件下载量的百分比进行加载,中部的文本控件用来现在文件下载的百分比,最下方的ImageView用来展示下载
2022-06-06

编程热搜

目录