流式上传与分片上传的原理与实现

在这里插入图片描述

🚀 博主介绍:大家好,我是无休居士!一枚任职于一线Top3互联网大厂的Java开发工程师! 🚀

🌟 在这里,你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人,我不仅热衷于探索一些框架源码和算法技巧奥秘,还乐于分享这些宝贵的知识和经验。

💡 无论你是刚刚踏入编程世界的新人,还是希望进一步提升自己的资深开发者,在这里都能找到适合你的内容。我们共同探讨技术难题,一起进步,携手度过互联网行业的每一个挑战

📣 如果你觉得我的文章对你有帮助,请不要吝啬你的点赞👍分享💕和评论哦! 让我们一起打造一个充满正能量的技术社区吧!


目录标题

  • 1. 背景
  • 2. 引言
  • 3. 流式上传
    • 3.1 原理解析
      • 3.1.1 实现步骤
      • 3.1.2 示例代码
        • 客户端(JavaScript)
        • 服务器端(Node.js + Express)
  • 4. 分片上传
    • 4.1 原理解析
      • 4.1.1 实现步骤
      • 4.1.2 示例代码
        • 客户端(JavaScript)
        • 服务器端(Node.js + Express)
  • 5. 断点续传💡
    • 5.1 原理解析
      • 5.1.1 实现步骤
      • 5.1.2 示例代码
        • 客户端(JavaScript)
        • 服务器端(Node.js + Express)
  • 6. 总结
  • 7. 附录
    • 7.1 流程图
      • 7.1.1 流式上传流程图
      • 7.1.2 分片上传流程图
      • 7.1.3 断点续传流程图
  • 8. 参考资料


1. 背景

在现代互联网应用中,文件上传是一个常见的需求,尤其是在处理大文件时。流式上传和分片上传是两种重要的文件上传技术。流式上传通过一次请求直接将文件上传到服务器,适用于小文件或网络环境稳定的场景。分片上传则是将大文件切成多个小的分片,每个分片单独上传,可以提高上传的可靠性和效率,特别适合网络环境不稳定或文件较大的情况。理解这两种技术的原理和实现,不仅能帮助开发者优化文件上传的性能,还能在面试中展示深厚的技术功底。

2. 引言

在互联网大厂的面试中,文件上传是一个常见的技术考点。特别是在处理大文件上传时,流式上传和分片上传是两个非常重要的技术手段。本文将详细介绍这两种上传方式的原理、实现细节以及如何实现断点续传,帮助你在面试中脱颖而出。🚀


3. 流式上传

3.1 原理解析

流式上传(Stream Upload)是指通过一次请求将文件直接上传到服务器。这种方式适用于小文件上传,因为小文件的传输时间较短,网络波动对其影响较小。然而,对于大文件,直接上传可能会因为网络中断、超时等原因导致上传失败。因此,流式上传更适合小文件或网络环境稳定的场景。🌟

3.1.1 实现步骤

  1. 客户端准备

    • 读取文件内容,将其转换为二进制数据。
    • 使用HTTP POST请求将文件数据发送到服务器。
  2. 服务器端处理

    • 接收客户端发送的文件数据。
    • 将接收到的数据保存到指定的文件路径。

3.1.2 示例代码

客户端(JavaScript)
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const formData = new FormData();
formData.append('file', file);

fetch('/upload', {
  method: 'POST',
  body: formData
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
服务器端(Node.js + Express)
const express = require('express');
const app = express();
const multer = require('multer');

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/');
  },
  filename: function (req, file, cb) {
    cb(null, Date.now() + '-' + file.originalname);
  }
});

const upload = multer({ storage: storage });

app.post('/upload', upload.single('file'), (req, res) => {
  res.send('File uploaded successfully');
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

4. 分片上传

4.1 原理解析

分片上传(Chunked Upload)是将大文件切成多个小的分片,每个分片单独上传到服务器。这种方式可以提高上传的可靠性和效率,特别是在网络环境不稳定的情况下。分片上传的关键在于如何处理分片的合并和断点续传。🌐

4.1.1 实现步骤

  1. 客户端准备

    • 读取文件内容,将其切成多个分片。
    • 为每个分片生成唯一的标识(如索引)。
    • 使用HTTP POST请求将每个分片单独上传到服务器。
  2. 服务器端处理

    • 接收客户端发送的分片数据。
    • 将接收到的分片数据保存到临时文件夹。
    • 当所有分片上传完成后,将分片合并成完整的文件。

4.1.2 示例代码

客户端(JavaScript)
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const chunkSize = 1024 * 1024; // 1MB
const chunks = Math.ceil(file.size / chunkSize);

for (let i = 0; i < chunks; i++) {
  const start = i * chunkSize;
  const end = Math.min(start + chunkSize, file.size);
  const chunk = file.slice(start, end);
  const formData = new FormData();
  formData.append('file', chunk);
  formData.append('index', i);
  formData.append('totalChunks', chunks);

  fetch('/upload-chunk', {
    method: 'POST',
    body: formData
  })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));
}
服务器端(Node.js + Express)
const express = require('express');
const app = express();
const multer = require('multer');
const fs = require('fs');
const path = require('path');

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/temp/');
  },
  filename: function (req, file, cb) {
    cb(null, `${req.body.index}-${Date.now()}-${file.originalname}`);
  }
});

const upload = multer({ storage: storage });

app.post('/upload-chunk', upload.single('file'), (req, res) => {
  const { index, totalChunks } = req.body;
  const filePath = path.join(__dirname, 'uploads/temp', `${index}-${Date.now()}-${req.file.originalname}`);

  if (parseInt(index) === parseInt(totalChunks) - 1) {
    mergeChunks(totalChunks, req.file.originalname);
  }

  res.send('Chunk uploaded successfully');
});

function mergeChunks(totalChunks, fileName) {
  const tempDir = path.join(__dirname, 'uploads/temp');
  const targetPath = path.join(__dirname, 'uploads', fileName);

  const writeStream = fs.createWriteStream(targetPath);

  for (let i = 0; i < totalChunks; i++) {
    const chunkPath = path.join(tempDir, `${i}-${Date.now()}-${fileName}`);
    const readStream = fs.createReadStream(chunkPath);
    readStream.pipe(writeStream, { end: false });

    readStream.on('end', () => {
      fs.unlinkSync(chunkPath);
    });
  }

  writeStream.end();
}

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

5. 断点续传💡

5.1 原理解析

断点续传是指在文件上传过程中,如果发生中断,可以从断点继续上传,而不是从头开始。这对于大文件上传尤为重要,因为它可以节省时间和带宽,提高上传的可靠性。

5.1.1 实现步骤

  1. 客户端准备

    • 读取文件内容,将其切成多个分片
    • 为每个分片生成唯一的标识(如索引)
    • 记录已上传的分片信息(如已上传的分片索引)
    • 使用HTTP POST请求将每个分片单独上传到服务器
  2. 服务器端处理

    • 接收客户端发送的分片数据。
    • 将接收到的分片数据保存到临时文件夹
    • 当所有分片上传完成后,将分片合并成完整的文件
    • 如果上传中断,客户端可以查询已上传的分片信息,从断点继续上传。

5.1.2 示例代码

客户端(JavaScript)
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];
const chunkSize = 1024 * 1024; // 1MB
const chunks = Math.ceil(file.size / chunkSize);

let uploadedChunks = [];

// 查询已上传的分片
fetch('/uploaded-chunks', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json'
  }
})
.then(response => response.json())
.then(data => {
  uploadedChunks = data.uploadedChunks;
  uploadChunks();
})
.catch(error => console.error('Error:', error));

function uploadChunks() {
  for (let i = 0; i < chunks; i++) {
    if (!uploadedChunks.includes(i)) {
      const start = i * chunkSize;
      const end = Math.min(start + chunkSize, file.size);
      const chunk = file.slice(start, end);
      const formData = new FormData();
      formData.append('file', chunk);
      formData.append('index', i);
      formData.append('totalChunks', chunks);

      fetch('/upload-chunk', {
        method: 'POST',
        body: formData
      })
      .then(response => response.json())
      .then(data => {
        console.log(data);
        uploadedChunks.push(i);
        if (uploadedChunks.length === chunks) {
          console.log('All chunks uploaded successfully');
        } else {
          uploadChunks();
        }
      })
      .catch(error => console.error('Error:', error));
    }
  }
}
服务器端(Node.js + Express)
const express = require('express');
const app = express();
const multer = require('multer');
const fs = require('fs');
const path = require('path');

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/temp/');
  },
  filename: function (req, file, cb) {
    cb(null, `${req.body.index}-${Date.now()}-${file.originalname}`);
  }
});

const upload = multer({ storage: storage });

app.post('/upload-chunk', upload.single('file'), (req, res) => {
  const { index, totalChunks } = req.body;
  const filePath = path.join(__dirname, 'uploads/temp', `${index}-${Date.now()}-${req.file.originalname}`);

  if (parseInt(index) === parseInt(totalChunks) - 1) {
    mergeChunks(totalChunks, req.file.originalname);
  }

  res.send('Chunk uploaded successfully');
});

app.get('/uploaded-chunks', (req, res) => {
  const tempDir = path.join(__dirname, 'uploads/temp');
  const files = fs.readdirSync(tempDir);
  const uploadedChunks = files.map(file => parseInt(file.split('-')[0]));
  res.json({ uploadedChunks });
});

function mergeChunks(totalChunks, fileName) {
  const tempDir = path.join(__dirname, 'uploads/temp');
  const targetPath = path.join(__dirname, 'uploads', fileName);

  const writeStream = fs.createWriteStream(targetPath);

  for (let i = 0; i < totalChunks; i++) {
    const chunkPath = path.join(tempDir, `${i}-${Date.now()}-${fileName}`);
    const readStream = fs.createReadStream(chunkPath);
    readStream.pipe(writeStream, { end: false });

    readStream.on('end', () => {
      fs.unlinkSync(chunkPath);
    });
  }

  writeStream.end();
}

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

6. 总结

通过本文的详细讲解,相信你已经对流式上传和分片上传有了全面的理解。流式上传适用于小文件或网络环境稳定的场景,而分片上传则更适合大文件上传,特别是需要处理网络中断的情况。断点续传是分片上传的一个重要特性,可以大大提高上传的可靠性和效率。希望本文能帮助你在面试中顺利过关,祝你好运!🎉


7. 附录

7.1 流程图

7.1.1 流式上传流程图

客户端读取文件
生成二进制数据
发送HTTP POST请求
服务器接收文件数据
保存文件到指定路径

7.1.2 分片上传流程图

客户端读取文件
切分成多个分片
生成分片标识
发送每个分片
服务器接收分片
保存分片到临时文件夹
合并分片成完整文件

7.1.3 断点续传流程图

客户端查询已上传分片
读取文件并切分成多个分片
生成分片标识
发送每个分片
服务器接收分片
保存分片到临时文件夹
合并分片成完整文件
客户端记录已上传分片

8. 参考资料

  • MDN Web Docs - Fetch API
  • Express.js - Node.js web application framework
  • Multer - Node.js middleware for handling multipart/form-data

乐于分享和输出干货的WXGZG:JavaPersons

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Ettus USRP X410

总线连接器: 以太网 RF频率范围: 1 MHz 至 7.2 GHz GPSDO: 是 输出通道数量: 4 RF收发仪瞬时带宽: 400 MHz 输入通道数量: 4 FPGA: Zynq US RFSoC (ZU28DR) 1 MHz to 7.2 GHz&#xff0c;400 MHz带宽&#xff0c;GPS驯服OCXO&#xff0c;USRP软件无线电设备 Ettus USRP X410集…

oracle 19c RAC到单机ogg部署安装

源端&#xff08;RAC&#xff09;目标端&#xff08;FS&#xff09;IP192.168.40.30/31192.168.40.50数据库版本Oracle 19.3.0Oracle 19.3.0主机名hfdb30/hfdb31hfogg操作系统REHL7.6REHL7.6数据库实例hfdb1/hfdb2hfogg同步用户hfdb1hfdb1同步表testtestOGG版本19.1.0.0.419.1.…

现代密码学

概论 计算机安全的最核心三个关键目标&#xff08;指标&#xff09;/为&#xff1a;保密性 Confidentiality、完整性 Integrity、可用性 Availability &#xff0c;三者称为 CIA三元组 数据保密性&#xff1a;确保隐私或是秘密信息不向非授权者泄漏&#xff0c;也不被非授权者使…

QT QGridLayout控件 全面详解

本系列文章全面的介绍了QT中的57种控件的使用方法以及示例&#xff0c;包括 Button(PushButton、toolButton、radioButton、checkBox、commandLinkButton、buttonBox)、Layouts(verticalLayout、horizontalLayout、gridLayout、formLayout)、Spacers(verticalSpacer、horizonta…

Adobe Illustrator 2024 安装教程与下载分享

介绍一下 下载直接看文章末尾 Adobe Illustrator 是一款由Adobe Systems开发的矢量图形编辑软件。它广泛应用于创建和编辑矢量图形、插图、徽标、图标、排版和广告等领域。以下是Adobe Illustrator的一些主要特点和功能&#xff1a; 矢量绘图&#xff1a;Illustrator使用矢量…

IDEA2023设置控制台日志输出到本地文件

1、Run->Edit Configurations 2、选择要输出日志的日志&#xff0c;右侧&#xff0c;IDEA2023的Logs在 Modify option 里 选中就会展示Logs栏。注意一定要先把这个日志文件创建出来&#xff0c;不然不会自动创建日志文件的 IDEA以前版本的Logs会直接展示出来 3、但是…

[UE5学习] 一、使用源代码安装UE5.4

一、简介 本文介绍了如何使用源代码安装编译UE5.4&#xff0c;并且新建简单的项目&#xff0c;打包成安卓平台下的apk安装包。 二、使用源代码安装UE5.4 注意事项&#xff1a; 请保证可以全程流畅地科学上网。请保证C盘具有充足的空间。请保证接下来安装下载的visual studi…

细说敏捷:敏捷四会之standup meeting

上一篇文章中&#xff0c;我们讨论了 敏捷四会 中 冲刺计划会 的实施要点&#xff0c;本篇我们继续分享敏捷四会中实施最频繁&#xff0c;团队最容易实施但往往也最容易走形的第二个会议&#xff1a;每日站会 关于每日站会的误区 站会是一个比较有标志性的仪式活动&#xff0…

10M和100M网口的编码及EMC影响

10M网口编码技术 10M网口&#xff0c;即10Base-T&#xff0c;采用的是曼彻斯特编码方法 。在这种编码中&#xff0c;“0”由“”跳变到“-”&#xff0c;而“1”由“-”跳变到“” 。这种编码方式的特点是信号的DC平衡&#xff0c;即信号在任何一段时间内的平均电压为零&#…

docker基本使用

参考视频&#xff1a; 参考视频https://www.bilibili.com/video/BV1e64y1F7pJ/?share_sourcecopy_web&vd_source8fc0c76c477d3db71f89fa5ae5b258c7 docker容器操作&#xff1a; 拉取镜像&#xff1a; 拉取官网ubuntu镜像 sudo docker pull ubuntu 运行镜像&#xff1a;…

音频信号采集前端电路分析

音频信号采集前端电路 一、实验要求 要求设计一个声音采集系统 信号幅度&#xff1a;0.1mVpp到1Vpp 信号频率&#xff1a;100Hz到16KHz 搭建一个带通滤波器&#xff0c;滤除高频和低频部分 ADC采用套件中的AD7920&#xff0c;转换率设定为96Ksps &#xff1b;96*161536 …

构建高效在线教育:SpringBoot课程管理系统

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理在线课程管理系统的相关信息成为必然。开发…

【云计算网络安全】解析 Amazon 安全服务:构建纵深防御设计最佳实践

文章目录 一、前言二、什么是“纵深安全防御”&#xff1f;三、为什么有必要采用纵深安全防御策略&#xff1f;四、以亚马逊云科技为案例了解纵深安全防御策略设计4.1 原始设计缺少安全策略4.2 外界围栏构建安全边界4.3 访问层安全设计4.4 实例层安全设计4.5 数据层安全设计4.6…

基于LiteFlow的风控系统指标版本控制

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 更新日志 最近关于https://github.com/wnhyang/coolGuard此项目更新了如下内容&#xff1a;https://g…

Spring:AOP切入点表达式

对于AOP中切入点表达式&#xff0c;我们总共会学习三个内容&#xff0c;分别是语法格式、通配符和书写技巧。 语法格式 首先我们先要明确两个概念: 切入点:要进行增强的方法切入点表达式:要进行增强的方法的描述方式 对于切入点的描述&#xff0c;我们其实是有两中方式的&a…

docker搭建私有的仓库

docker搭建私有仓库 一、为什么要搭建私有的仓库&#xff1f; 因为在国内&#xff0c;访问&#xff1a;https://hub.docker.com/ 会出现无法访问页面。。。。&#xff08;已经使用了魔法&#xff09; 当然现在也有一些国内的镜像管理网站&#xff0c;比如网易云镜像服务、Dao…

大白话讲Promise(最详细)

学前端的大家都知道promise是重中之重&#xff0c;也是面试的必考项。但是刚接触promise我一直很晕头晕脑的&#xff0c;搜集文章前看后看基本都是讲解promise的状态它的方法就没有再深入了&#xff0c;以至于面试时候面试官一旦往深问我就懵了。所以今天我们就详细的说一下pro…

【笔记】自动驾驶预测与决策规划_Part7_数据驱动的预测方法

文章目录 0. 前言1. 多模态传感器的编码方式1.1 栅格化表示1.2 向量化表示 Vectornet1.3 基于点云或者多模态输入的预测1.4 基于Transformer的方法 2. 网络输出的表达形式2.1 多模态轨迹回归2.2 轨迹分类2.3 轨迹回归轨迹分类2.4 目标点预测 3.场景级别的预测和决策3.1 论文&am…

回溯法经典难题解析

本文将通过几个经典的回溯问题&#xff0c;展示回溯算法的应用及其在解决问题时的核心思想和技巧。这些问题包括全排列、全排列II、N皇后以及数独问题&#xff0c;本文将分别介绍每个问题的思路与实现。 46. 全排列 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有…

无线图传下的低延迟视频传输播放技术探讨

技术背景 无线图传技术即无线图像传输技术&#xff0c;是指不用布线&#xff08;线缆&#xff09;利用无线电波来传输图像数据的技术。 一、工作原理 无线图传技术主要涉及图像采集、编码、调制、发射、接收、解调、解码和图像显示等环节。 图像采集&#xff1a;通过摄像头…