大文件上传切片上传

news2024/9/24 9:26:12

大文件上传&切片上传

简历中如何写项目经验&技术

切片上传相关面试题

基础实现流程

实现核心:秒传 断点续传 切片 …… 如何实现的

最基本的视图

加上拖拽

加上拖拽事件。监听drop事件,event.dataTransfer.files文件对象。其他dragenter dragover dragleave事件event.stopPropagation event.preventDefault

文件预览。URL.createObjectUrl

切片上传实现

根据文件内容得到加密文件名。

file->arrayBuffer->加密buffer->buffer.toString

优化点:放到webworker里不会使用js内存

文件切片 blob.slice

并行上传每个分片。优化点:并发管控

node层:核心的2个接口

上传单个切片的接口

合并切片的接口

const express = require('express');
const logger = require('morgan');
const { StatusCodes } = require('http-status-codes');
const cors = require('cors');
const fs = require('fs-extra');
const path = require('path');
const PUBLIC_DIR = path.resolve(__dirname, 'public');
const TEMP_DIR = path.resolve(__dirname, 'temp');
const CHUNK_SIZE = 100 * 1024 * 1024;
//存放上传并合并好的文件
fs.ensureDirSync(PUBLIC_DIR);
//存放分片的文件
fs.ensureDirSync(TEMP_DIR);
const app = express();
app.use(logger('dev'));
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.resolve(__dirname, 'public')));
/**
 * 上传分片
 */
app.post('/upload/:filename', async (req, res, next) => {
    //通过路径参数获取文件名
    const { filename } = req.params;
    //通过查询参数获取分片名
    const { chunkFileName } = req.query;
    //写入文件的起始位置
    const start = isNaN(req.query.start)?0:parseInt(req.query.start,10);
    //创建用户保存此文件的分片的目录
    const chunkDir = path.resolve(TEMP_DIR, filename);
    //分片的文件路径
    const chunkFilePath = path.resolve(chunkDir, chunkFileName);
    //先确定分片目录存在
    await fs.ensureDir(chunkDir);
    //创建此文件的可写流 ,可以指定写入的起始位置
    const ws = fs.createWriteStream(chunkFilePath, {start,flags:'a'});
    //后面会实现暂停操作,如果客户端点击了暂停按钮,会取消上传的操作,取消之后会在服务器触发请求择象的
    //aborted事件,关闭可定流
    req.on('aborted', () => { ws.close() });
    //使用管道的方式把请求中的请求体流数据写入到文件中
    try {
        await pipeStream(req, ws);
        res.json({ success: true });
    } catch (error) {
        next(error);
    }
});

app.get('/merge/:filename', async (req, res, next) => {
    //通过路径参数获取文件名
    const { filename } = req.params;
    try {
        await mergeChunks(filename);
        res.json({ success: true });
    } catch (error) {
        next(error)
    }
});
app.get('/verify/:filename',async (req,res,next)=>{
    const {filename} = req.params;
    //先获取文件在服务器的路径
    const filePath = path.resolve(PUBLIC_DIR,filename);
    //判断是文件在服务器端是否存在
    const isExist = await fs.pathExists(filePath);
    //如果已经存在了,则直接返回不需要上传了
    if(isExist){
        return res.json({success:true,needUpload:false});
    }
    const chunksDir = path.resolve(TEMP_DIR,filename);
    const existDir = await fs.pathExists(chunksDir);
    //存放已经上传的分片的对象数组
    let uploadedChunkList =[];
    if(existDir){
        //读取临时目录里面的所有的分片对应的文件
        const chunkFileNames = await fs.readdir(chunksDir);
        //读取每个分片文件的文件信息,主要是它的文件大小,表示已经上传的文件的大小
        uploadedChunkList = await Promise.all(chunkFileNames.map(async function(chunkFileName){
            const {size} = await fs.stat(path.resolve(chunksDir,chunkFileName));
            return {chunkFileName,size};
        }));
    }
    //如果没有此文件,则意味着服务器还需要你上传此文件
    //返回,上传尚未完成,但是已经上传了一部分了,我把已经上传的分片名,以及分片的大小给客户端
    //客户端可以只上传分片剩下的数据部就可以了
    res.json({success:true,needUpload:true,uploadedChunkList});
});
async function mergeChunks(filename) {
    const mergedFilePath = path.resolve(PUBLIC_DIR, filename);
    const chunkDir = path.resolve(TEMP_DIR, filename);
    const chunkFiles = await fs.readdir(chunkDir);
    //对分片按索引进行升序排列
    chunkFiles.sort((a, b) => Number(a.split('-')[1]) - Number(b.split('-')[1]));
    try {
        //为了提高性能,我们在这时可以分片并行写入
        const pipes = chunkFiles.map((chunkFile, index) => {
            return pipeStream(
                fs.createReadStream(path.resolve(chunkDir, chunkFile), { autoClose: true }),
                fs.createWriteStream(mergedFilePath, { start: index * CHUNK_SIZE })
            );
        });
        //并发把每个分片的数据写入到目标文件中
        await Promise.all(pipes);
        //删除分片的文件和文件夹
        await fs.rmdir(chunkDir, { recursive: true })
        //合并完文件之后可以重新在这里计算合并后的文件的hash值,和文件中的hash值进行对比
        //如果值是一样的,说明肯定内容是对的,没有被修改
    } catch (error) {
        next(error)
    }
}
function pipeStream(rs, ws) {
    return new Promise((resolve, reject) => {
        //把可读流中的数据写入可写流中
        rs.pipe(ws).on('finish', resolve).on('error', reject);
    });
}
app.listen(8080, () => console.log('Sever started on port 8080'));

进度是每个切片的进度

秒传:已经存在就不传了

取消上传:cancelToken

断点续传:用户点了暂停/网络断开的情况。

优化点:

把耗时的操作放到worker里,不要阻塞主进程

let worker=new Worker('./fileCalculate')

worker.postMessage self.on('message')

如果失败重试3次:但是做的是整个失败重试

待考虑问题:

如何确定切片大小?

如何做文件校验?

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

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

相关文章

odoo17基础培训1-odoo开发基础知识准备以及odoo17开发环境安装

odoo17基础培训 一、odoo开发基础知识准备以及odoo17开发环境安装 1、odoo是什么? 当我介绍客户使用odoo系统作为业务管理平台时,有时会被问到Odoo是什么? 简单点,可以这么说: Odoo是一套完整的系统,是…

Pycharm安装numpy库失败解决办法

一、出现错误(以matplotlib为例): 二、解决办法: 方法一(失败):PyCharm中有一个安装库的方法是:Settings>>Python Interpreter>>点击右侧的加号 第二个图 失败原因&am…

【开源】基于JAVA语言的固始鹅块销售系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 鹅块类型模块2.3 固始鹅块模块2.4 鹅块订单模块2.5 评论管理模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 鹅块类型表3.2.2 鹅块表3.2.3 鹅块订单表3.2.4 鹅块评论表 四、系统展示五、核心代码5.…

x-cmd pkg | jless - 受 Vim 启发的命令行 JSON 查看器

目录 简介首次用户功能特点类似工具与竞品进一步探索 简介 jless 是一个命令行 JSON 查看器,设计用于读取、探索和搜索 JSON 数据。可以使用它来替代 less 、 jq 、 cat 以及您当前用于查看 JSON 文件的编辑器的任何组合。它是用 Rust 编写的,可以作为单…

确保CentOS系统中的静态HTTP服务器的数据安全

确保CentOS系统中的静态HTTP服务器的数据安全是一项重要的任务,它有助于保护网站免受未经授权的访问、数据泄露和其他安全威胁。以下是一些关键步骤和最佳实践,以确保CentOS系统中静态HTTP服务器的数据安全: 限制访问权限确保只有授权用户可…

Python爬虫---scrapy shell 调试

Scrapy shell是Scrapy提供的一个交互式shell工具,它可以帮助我们进行爬虫的开发和调试。可以使用它来测试xpath或css表达式,查看它们是如何工作的,以及它们从你试图抓取的网页中提取的数据。它允许你在编写spider时交互地测试表达式&#xff…

JFinal学生信息管理系统

JFinal学生信息管理系统 项目地址:mendianyu/StudentManage: JFinal学生信息管理系统 (github.com) 环境介绍: IDE:IDEA 2021.2.3 jdk:1.8 maven:3.6.3 项目介绍:JFinal框架实现的学生信息管理系统,完成简单的学生信…

MATLAB | 龙年大吉,使用MATLAB绘制会动的中国风神龙

hey各位好久不见,龙年到了,这期画一期配色非常中国风的龙,这个造型的龙参考了某些html绘制龙的视频,但是由于html版全网都是也不咋给代码和代码出处,因此自己写了个MATLAB版本: 可以看到还是非常酷炫的&…

Vulnhub靶机:driftingblues 3

一、介绍 运行环境:Virtualbox 攻击机:kali(10.0.2.15) 靶机:driftingblues3(10.0.2.19) 目标:获取靶机root权限和flag 靶机下载地址:https://www.vulnhub.com/entr…

利用C语言实现输出杨辉三角的前10行

杨辉三角的特点 第一列都为1&#xff0c;对角线为1 第x行第x列为1 第几行就有几个元素 从第三行开始,第二列的元素等于第二行的第一列元素第二列元素之和(排除从第三行开始的首和尾元素) //用C语言实现打印出10行杨辉三角 #include<stdio.h>int main(){int a[10][10];//…

膜结构球形影院为观众打造观影新体验

在数字科技快速发展的当下&#xff0c;轻空间公司打破传统影院的束缚&#xff0c;领航未来娱乐体验的创新浪潮。膜结构球形影院问世&#xff0c;它不仅仅是一个娱乐场所&#xff0c;更是一场極致沉浸感的感官之旅&#xff0c;为观众带来震撼性的视听冲击。 沉浸式体验的新纪元 …

【HuggingFace Transformer库学习笔记】基础组件学习:Trainer

基础组件学习——Trainer 导入包 from transformers import AutoTokenizer, AutoModelForSequenceClassification, Trainer, TrainingArguments from datasets import load_dataset加载数据集 dataset load_dataset("csv", data_files"./ChnSentiCorp_htl_al…

【亲测可行】如何申请并登录腾讯云免费服务器

腾讯云免费服务器申请入口 https://curl.qcloud.com/FJhqoVDP 免费服务器可选轻量应用服务器和云服务器CVM&#xff0c;轻量配置可选2核2G3M、2核8G7M和4核8G12M&#xff0c;CVM云服务器可选2核2G3M和2核4G3M配置&#xff0c;腾讯云百科txybk.com分享2024年最新腾讯云免费服务器…

MySQL 基于 GTID 主从复制

GTID 定义 GTID 是 MySQL 事务标识&#xff0c;为每一个提交的事务都生成一个标识&#xff0c;并且是全局唯一的&#xff0c;这个特性是从 MySQL5.6 引进的。 组成 GTID 是由 UUID TID&#xff0c;UUID 是MySQL的唯一标识&#xff0c;每个MySQL实例之间都是不同的。TID是代表…

力扣(leetcode)第500题键盘行(Python)

500.键盘行 题目链接&#xff1a;500.键盘行 给你一个字符串数组 words &#xff0c;只返回可以使用在 美式键盘 同一行的字母打印出来的单词。键盘如下图所示。 美式键盘 中&#xff1a; 第一行由字符 “qwertyuiop” 组成。 第二行由字符 “asdfghjkl” 组成。 第三行由字…

Mybatis实现映射,一次查询和嵌套查询

1.实现映射 Mybatis的最大魅力就在于它的语句映射。实现映射一般有一下三种方法&#xff1a; 当我们在数据库的列名和java中的属性名完全相同时&#xff0c;mybatis会自动映射并将查询结果封装。 对于由多个单词组成的名字时&#xff08;例如studentgender&#xff09;&…

C++ Builder XE关于TDateTime批量加减时间TTimerPiker的设置

void __fastcall TForm3::Button3Click(TObject *Sender) { TDateTime *DT new TDateTime(); //new TDateTime类型变量 *DTTPicker1->Time; //加 1 小时: DT (double)DT 1/24; //加 1 分钟: DT (double)DT 1/(24*60); //加 1 秒钟: DT (double)DT 1/(24*60*60); …

NUS CS1101S:SICP JavaScript 描述:三、模块化、对象和状态

原文&#xff1a;3 Modularity, Objects, and State 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 变化中安宁 &#xff08;即使它在变化&#xff0c;它仍然保持不变。&#xff09; ——赫拉克利特 变化越大&#xff0c;越是相同。 ——阿方斯卡尔 前面的章节介绍了构…

基于MAP算法的Turbo译码 -- 公式推导

到此为止&#xff0c;讲完了turbo译码器的子译码器基于MAP算法的译码过程。但在实际使用中&#xff0c;很少直接使用MAP算法进行译码。而是使用改进的LOG-MAP和MAX-LOG-MAP算法进行译码&#xff0c;因此译码的整体流程&#xff0c;包括外信息的计算以及先验信息的获取等。都在后…

SSM整合(实现简单查询功能)

在名为ssm的数据库内创建表 CREATE TABLE account (id int(11) NOT NULL AUTO_INCREMENT,name varchar(20) DEFAULT NULL,money double DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; 创建工程 pom.xml <?xml version"1.0" encoding&quo…