Express(二):文件下载 - 分片下载

news2024/9/24 22:57:39

效果(比较下载速度)

在这里插入图片描述

服务端

请求响应头

在这里插入图片描述

源码

const express = require('express');
const path = require("path");
const fs = require("fs");
const router = express.Router();

const fileName = '下载文件.zip';
const filePath = path.join(__dirname, `../public/${fileName}`);

router.get('/size', (req, res, next) => {
	res.setHeader('Access-Control-Allow-Origin', '*');
	res.json({code: 200, data: {fileName, fileSize: fs.statSync(filePath).size}, message: '请求成功!'});
});

router.get('/download', (req, res, next) => {
	res.setHeader('Access-Control-Allow-Origin', '*');
	// acceptRanges 启用或禁用接受范围请求,默认为true。禁用此选项将不会发送Accept Ranges并忽略Range请求标头的内容。
	res.download(filePath, fileName, {acceptRanges: true});
});

module.exports = router;

客户端

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%= title %></title>
</head>
<style>
    div.progress {
        width: 0;
        margin: 10px;
        height: 20px;
        padding: 0 10px;
        font-size: 12px;
        line-height: 20px;
        white-space: nowrap;
        border: 1px solid #cccccc;
        background-color: greenyellow;
    }
</style>
<body>

<button onclick="basicDownloadFile();fragmentDownloadFile();">下载文件(普通、分片)</button>

<script>
	// 请求
	function request({method, url, headers = {}, responseType = undefined}) {
		let div = null;
		let width = 0;
		return new Promise((resolve, reject) => {
			const xhr = new XMLHttpRequest();
			xhr.open(method, url);
			for (const key in headers) {
				xhr.setRequestHeader(key, headers[key]);
			}
			if (responseType) {
				xhr.responseType = responseType;
			}
			xhr.send();
			xhr.onreadystatechange = (e) => {
				if (e.target.readyState === 2) {
					div = document.createElement('div');
					div.setAttribute('class', 'progress');
					div.innerText = `用时 -> ${method} ${url} ${headers.Range || ''}`;
					document.body.appendChild(div);
				}
				if (e.target.readyState === 3) {
					width++;
					div.style.width = `${width}px`;
				}
				// console.log(e.target, e.target.readyState)
				if (e.target.readyState === 4) {
					if (e.target.status >= 200 && e.target.status < 300) {
						console.log(width);
						resolve(e.target.response);
					}
				}
			}
		})
	}

	// 普通下载文件
	function basicDownloadFile() {
		request({
			method: 'GET',
			url: `http://127.0.0.1:3000/file/download?_t=${Date.now()}`,
			responseType: 'arraybuffer',
		})
			.then(res => {
				// 一个不可变、原始数据的类文件对象
				const blob = new Blob([res]);
				// 下载
				const a = document.createElement('a');
				const href = URL.createObjectURL(blob);
				a.href = href;
				a.download = '下载文件.zip';
				document.body.appendChild(a);
				a.click();
				document.body.removeChild(a);
				window.URL.revokeObjectURL(href);
				console.log('下载完成01')
			})
	}

	// 分片下载文件
	async function fragmentDownloadFile() {
		let res = await request({method: 'GET', url: `http://127.0.0.1:3000/file/size?_t=${Date.now()}`});
		// 文件大小
		const {fileSize, fileName} = JSON.parse(res).data;
		console.log('文件名称', fileName);
		console.log('文件大小', fileSize);
		// 步长
		const step = Math.pow(8, 6);
		console.log('步长', step);
		// 任务
		let tasks = [];
		// 切割任务
		for (let i = 0; i < Math.ceil(fileSize / step);) {
			let [start, end] = [i * step, Math.min(++i * step - 1, fileSize)];
			console.log([start, end]);
			tasks.push(request({
				method: 'GET',
				url: `http://127.0.0.1:3000/file/download?_t=${Date.now()}`,
				headers: {
					Range: `bytes=${start}-${end}`
				},
				responseType: 'arraybuffer'
			}));
		}
		console.log('步长切割的请求集合', tasks);
		// 原始二进制数据缓冲区
		const arrayBuffers = await Promise.all(tasks);
		console.log('原始二进制数据缓冲区集合', arrayBuffers);
		// 数组类型表示一个 8 位无符号整型数组
		const uint8Array = new Uint8Array(fileSize);
		let offset = 0;
		arrayBuffers.forEach(arrayBuffer => {
			uint8Array.set(new Uint8Array(arrayBuffer), offset);
			offset += arrayBuffer.byteLength;
		});
		console.log('ArrayBuffer', uint8Array.buffer);
		// 一个不可变、原始数据的类文件对象
		const blob = new Blob([uint8Array.buffer]);
		// 下载
		const a = document.createElement('a');
		const href = URL.createObjectURL(blob);
		a.href = href;
		a.download = fileName;
		document.body.appendChild(a);
		a.click();
		document.body.removeChild(a);
		window.URL.revokeObjectURL(href);
		console.log('下载完成02')
	}
</script>
</body>
</html>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1387792.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于LabVIEW的声音信号采集分析系统开发

摘要&#xff1a;以美国国家仪器(NI)公司开发的LabVIEW虚拟仪器为软件开发平台&#xff0c;设计了一个可以同步实现声音信号采集和分析的多功能模块化软件系统&#xff0e;借助LabVIEW图形化软件相应的声音读取、写入和存储函数实现对声音信号的采集、存储、时域分析和频域分析…

精确掌控并发:滑动时间窗口算法在分布式环境下并发流量控制的设计与实现

这是《百图解码支付系统设计与实现》专栏系列文章中的第&#xff08;15&#xff09;篇&#xff0c;也是流量控制系列的第&#xff08;2&#xff09;篇。点击上方关注&#xff0c;深入了解支付系统的方方面面。 上一篇介绍了固定时间窗口算法在支付渠道限流的应用以及使用redis…

Spring Boot异常处理!!!

SpringBoot默认的处理异常的机制&#xff1a;SpringBoot 默认的已经提供了一套处理异常的机制。一旦程序中出现了异常 SpringBoot 会向/error 的 url 发送请求。在 springBoot 中提供了一个叫 BasicErrorController 来处理/error 请求&#xff0c;然后跳转到默认显示异常的页面…

vue:使用【3.0】:条件模块

一、条件层级效果图 二、代码 <template><ContentWrap><!-- 添加条件分支:level1 --><div class"btnBox" v-if"isEdit"><el-button type"primary" click"add">添加条件分支</el-button></div…

如何优化大型语言模型,让AI回应更智能、更准确?

什么是检索增强生成&#xff08;RAG)&#xff1f; 检索增强生成&#xff08;RAG&#xff09;是一种优化大型语言模型输出的过程&#xff0c;它在生成回应之前会参考其训练数据源之外的权威知识库。大型语言模型&#xff08;LLM&#xff09;在大量数据上进行训练&#xff0c;使…

Windows10解决大小核调度问题

文章目录 1.开启高性能模式2.下载安装PowerSettingsExplorer3.修改配置生效的异类策略异类线程调度策略异类短时间线程调度策略 4.你的电源策略5.CPU展示 该教程是给笔记本电脑用的&#xff0c;经过我实践是成功的。 1.开启高性能模式 使用管理员模式的PowerShell输入下列指令 …

微信小程序上传并显示图片

实现效果&#xff1a; 上传前显示&#xff1a; 点击后可上传&#xff0c;上传后显示&#xff1a; 源代码&#xff1a; .wxml <view class"{{company_logo_src?blank-area:}}" style"position:absolute;top:30rpx;right:30rpx;height:100rpx;width:100rp…

C++ 设计模式之享元模式

【声明】本题目来源于卡码网&#xff08;题目页面 (kamacoder.com)&#xff09; 【提示&#xff1a;如果不想看文字介绍&#xff0c;可以直接跳转到C编码部分】 【简介】什么是享元模式 -- 可重复使用 享元模式是⼀种结构型设计模式&#xff0c;在享元模式中&#xff0c;对象被…

基于深度学习的桃子熟度与大小智能检测

基于深度学习的桃子熟度与大小智能检测 基于深度学习的桃子熟度与大小智能检测引言1. 环境搭建与准备2. 数据准备3. 模型准备4. 训练准备5. 服务器端部署结语 基于深度学习的桃子熟度与大小智能检测 引言 随着时代的快速发展&#xff0c;人工智能时代为中国农业带来了新的机遇…

Leetcode刷题【每日n题】(1)

目录 1.题目一 2.思路分析 3.代码实现 4.题目二 5.思路分析 6.代码实现 1.题目一 11. 盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴…

Vue-20、Vue.set()的使用

1、添加对象某个属性 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Vue.set()的使用</title><script type"text/javascript" src"https://cdn.jsdelivr.net/npm/vue2/dist…

2023.12.30性质

若连通图上各边的权值均不相同&#xff0c;则该图的最小生成树是唯一的。 由k算法&#xff0c;即由边从小到大的顺序构造&#xff0c;如果边权值各不相同&#xff0c;那么构造出来的最小生成树唯一&#xff0c;就是唯一的顺序&#xff0c;从小到大 关于带权无向图的最小生成…

动态pv(nfs方式挂载)

1、定义 发布pvc之后可以生成pv&#xff0c;还可以在共享服务器上直接生成挂载目录 pvc直接绑定和使用pv 2、动态pv依赖两个组件 &#xff08;1&#xff09;provisioner卷插件&#xff1a;k8s本身支持的动态pv创建不包括nfs&#xff0c;需要声明和安装一个外部插件provisio…

HDFS和MapReduce综合实训

文章目录 第1关&#xff1a;WordCount词频统计第2关&#xff1a;HDFS文件读写第3关&#xff1a;倒排索引第4关&#xff1a; 网页排序——PageRank算法 第1关&#xff1a;WordCount词频统计 测试说明 以下是测试样例&#xff1a; 测试输入样例数据集&#xff1a;文本文档test1…

canvas截取视频图像(图文示例)

查看专栏目录 canvas示例教程100专栏&#xff0c;提供canvas的基础知识&#xff0c;高级动画&#xff0c;相关应用扩展等信息。canvas作为html的一部分&#xff0c;是图像图标地图可视化的一个重要的基础&#xff0c;学好了canvas&#xff0c;在其他的一些应用上将会起到非常重…

[VGG团队论文阅读]Free3D: Consistent Novel View Synthesis without 3D Representation

Vedaldi, C. Z. A. (n.d.). Free3D: Consistent Novel View Synthesis without 3D Representation. Chuanxiaz.com. https://chuanxiaz.com/free3d/static/videos/Free3D.pdf Free3D: 无需3D表示的一致新视角合成 Visual Geometry Group, University of Oxford 摘要 我们介绍…

C# 面向切面编程之AspectCore初探

写在前面 AspectCore 是Lemon名下的一个国产Aop框架&#xff0c;提供了一个全新的轻量级和模块化的Aop解决方案。面向切面也可以叫做代码拦截&#xff0c;分为静态和动态两种模式&#xff0c;AspectCore 可以实现动态代理&#xff0c;支持程序运行时在内存中“临时”生成 AOP 动…

Mindspore 公开课 - prompt

prompt 介绍 Fine-Tuning to Prompt Learning Pre-train, Fine-tune BERT bidirectional transformer&#xff0c;词语和句子级别的特征抽取&#xff0c;注重文本理解Pre-train: Maked Language Model Next Sentence PredictionFine-tune: 根据任务选取对应的representatio…

Android 仿快手视频列表,RecyclerView与Banner联动效果

这是看到群里讨论过快手APP的一个观看他人视频列表的一个联动效果&#xff0c;但是并不是完全按照这个软件的效果来做的&#xff0c;只是参考&#xff0c;并不是完全仿照这个软件来做的&#xff0c;没时间去优化排版问题了&#xff0c;请见谅&#xff0c;如图&#xff1a; 实现…

pycharm管理仓库(Manager Repository)不见了

经常使用pycharm的大佬们都知道&#xff0c;pycharm中内置了很多库和包&#xff0c;很好用 但是下载来用自带的源很麻烦&#xff0c;于是就用国内的源 可以当我们添加管理仓库的时候&#xff0c;却没有了按钮&#xff0c;如何解决呢&#xff1f; 回到pycharm的主界面&#xf…