🚀 博主介绍:大家好,我是无休居士!一枚任职于一线Top3互联网大厂的Java开发工程师! 🚀
🌟 在这里,你将找到通往Java技术大门的钥匙。作为一个爱敲代码技术人,我不仅热衷于探索一些框架源码和算法技巧奥秘,还乐于分享这些宝贵的知识和经验。
💡 无论你是刚刚踏入编程世界的新人,还是希望进一步提升自己的资深开发者,在这里都能找到适合你的内容。我们共同探讨技术难题,一起进步,携手度过互联网行业的每一个挑战。
📣 如果你觉得我的文章对你有帮助,请不要吝啬你的点赞👍分享💕和评论哦! 让我们一起打造一个充满正能量的技术社区吧!
目录标题
- [1. 背景](#1. 背景)
- [2. 引言](#2. 引言)
- [3. 流式上传](#3. 流式上传)
-
- [3.1 原理解析](#3.1 原理解析)
-
- [3.1.1 实现步骤](#3.1.1 实现步骤)
- [3.1.2 示例代码](#3.1.2 示例代码)
-
- 客户端(JavaScript)
- [服务器端(Node.js + Express)](#服务器端(Node.js + Express))
- [4. 分片上传](#4. 分片上传)
-
- [4.1 原理解析](#4.1 原理解析)
-
- [4.1.1 实现步骤](#4.1.1 实现步骤)
- [4.1.2 示例代码](#4.1.2 示例代码)
-
- 客户端(JavaScript)
- [服务器端(Node.js + Express)](#服务器端(Node.js + Express))
- [5. 断点续传💡](#5. 断点续传💡)
-
- [5.1 原理解析](#5.1 原理解析)
-
- [5.1.1 实现步骤](#5.1.1 实现步骤)
- [5.1.2 示例代码](#5.1.2 示例代码)
-
- 客户端(JavaScript)
- [服务器端(Node.js + Express)](#服务器端(Node.js + Express))
- [6. 总结](#6. 总结)
- [7. 附录](#7. 附录)
-
- [7.1 流程图](#7.1 流程图)
-
- [7.1.1 流式上传流程图](#7.1.1 流式上传流程图)
- [7.1.2 分片上传流程图](#7.1.2 分片上传流程图)
- [7.1.3 断点续传流程图](#7.1.3 断点续传流程图)
- [8. 参考资料](#8. 参考资料)
1. 背景
在现代互联网应用中,文件上传是一个常见的需求,尤其是在处理大文件时。流式上传和分片上传是两种重要的文件上传技术。流式上传通过一次请求直接将文件上传到服务器,适用于小文件或网络环境稳定的场景。分片上传则是将大文件切成多个小的分片,每个分片单独上传,可以提高上传的可靠性和效率,特别适合网络环境不稳定或文件较大的情况。理解这两种技术的原理和实现,不仅能帮助开发者优化文件上传的性能,还能在面试中展示深厚的技术功底。
2. 引言
在互联网大厂的面试中,文件上传是一个常见的技术考点。特别是在处理大文件上传时,流式上传和分片上传是两个非常重要的技术手段。本文将详细介绍这两种上传方式的原理、实现细节以及如何实现断点续传,帮助你在面试中脱颖而出。🚀
3. 流式上传
3.1 原理解析
流式上传(Stream Upload)是指通过一次请求将文件直接上传到服务器。这种方式适用于小文件上传,因为小文件的传输时间较短,网络波动对其影响较小。然而,对于大文件,直接上传可能会因为网络中断、超时等原因导致上传失败。因此,流式上传更适合小文件或网络环境稳定的场景。🌟
3.1.1 实现步骤
-
客户端准备:
- 读取文件内容,将其转换为二进制数据。
- 使用HTTP POST请求将文件数据发送到服务器。
-
服务器端处理:
- 接收客户端发送的文件数据。
- 将接收到的数据保存到指定的文件路径。
3.1.2 示例代码
客户端(JavaScript)
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)
javascript
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 实现步骤
-
客户端准备:
- 读取文件内容,将其切成多个分片。
- 为每个分片生成唯一的标识(如索引)。
- 使用HTTP POST请求将每个分片单独上传到服务器。
-
服务器端处理:
- 接收客户端发送的分片数据。
- 将接收到的分片数据保存到临时文件夹。
- 当所有分片上传完成后,将分片合并成完整的文件。
4.1.2 示例代码
客户端(JavaScript)
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)
javascript
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 实现步骤
-
客户端准备:
读取文件内容,将其切成多个分片
。为每个分片生成唯一的标识(如索引)
。记录已上传的分片信息(如已上传的分片索引)
。使用HTTP POST请求将每个分片单独上传到服务器
。
-
服务器端处理:
- 接收客户端发送的分片数据。
将接收到的分片数据保存到临时文件夹
。当所有分片上传完成后,将分片合并成完整的文件
。如果上传中断,客户端可以查询已上传的分片信息,从断点继续上传。
5.1.2 示例代码
客户端(JavaScript)
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)
javascript
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