0%

文件上传

文件上传 功能在web开发中很常见,到目前为止,常见且使用较多的上传方式有 form表单FormDataBase64 ;比较新的上传方案还有 WebSocketWebRTC

我们先主要讲解 FormDataBase64 的上传方式,当然,这2种需要使用 Ajax 来进行提交,同时,我们将采用原生写法。

为什么用原生写法呢?只要你掌握原生写法,那么不管你用的什么第三方框架,都能写的出来。

后台我们使用 express 进行搭建。


初始化项目

  1. 新建一个文件夹 upload-file, 然后执行以下命令
npm init -y

yarn init -y
  1. upload-file 下创建以下文件和目录
pages
- index.html
upload
main.js
package.json
  1. 执行以下命令安装依赖包
npm i express multer body-parser -S

yarn add express multer body-parser

各个依赖包的作用:

  • express:搭建服务器
  • body-parser :处理请求参数
  • multer :处理文件上传

到此,我们的项目就初始化完成了


基本配置

HTML配置

打开 pages/index.html,在 body 内写入以下内容

<button id="btn">上传</button>

<script>
// 获取到我们的按钮元素
let btn = document.getElementById('btn');
</script>

我们在 body 标签内写了一个按钮,后续的上传操作,我们都通过点击这个按钮来完成


入口文件配置

打开我们都 main.js ,写入以下内容

const express = require('express')
const bodyParser = require('body-parser')
const path = require('path')
const fs = require('fs')
const multer = require('multer')
const app = express()

// 配置静态资源目录
app.use(express.static(path.resolve(__dirname, 'pages')))

// 配置请求参数解析
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))

// 服务器监听
app.listen(8080, () => {
console.log('http://127.0.0.1:8080');
})

通过上面的配置,我们的服务器就搭建起来来,我们在 upload-file 这个文件夹下,按下键盘都 shift + 鼠标右键, 选择 在此处打开 powershell

然后,我们去浏览器打开 http://127.0.0.1:8080 ,就能看到我们的HTML文件了。


上传方式

FormData

通过ajax提交 formdata 数据至后台,这种方案其实就跟我们直接用 form 表单提交文件是差不多的形式

1. 绑定点击事件

打开 index.html ,为我们的按钮绑定点击事件,事件函数内编写我们的 Ajax 代码

// pages/index.html

// 为按钮绑定点击事件
btn.addEventListener('click', () => {
// 1. 创建 input 标签
let fileInput = document.createElement('input');

// 2. 设置type为文件类型
fileInput.type = 'file';

// 3. 触发file控件的点击事件
fileInput.click();

// 4. 为input绑定change事件,当用户选择好文件后,我们可以得到file对象
fileInput.addEventListener('change', function () {
// 5. 判断是否存在文件
if (!this.files.length) return;

// 6. 取出 file 对象
let file = this.files[0];

// 7. 调用formdata上传
sendFormData(file);
})
})

// 我们的上传函数
function sendFormData(){
// 1. 得到ajax实例对象
let xhr = new XMLHttpRequest();

// 2. 得到 FormData 实例对象
let formdata = new FormData();

// 3. 往formdata中添加文件
formdata.append('file', data);

// 4. 配置请求方法和请求路径
xhr.open('post', '/upload1');

// 5. 将我们的formdata发送至后台
xhr.send(formdata);

// 6. 监听请求状态,onload为请求完成
xhr.onload = function () {
// 7. 取出后台返回的json数据
let res = JSON.parse(this.response);

// 8. 判断后台传递回来的状态是否为true
if (res.state === true) {
alert('上传成功');
} else {
alert('上传失败');
}
}
}

通过Ajax方式进行文件上传的话,必须要用 FormData 进行上传

注意:IE10以下的浏览器,不支持 FormData

2. 添加后台接口

打开 main.js ,添加一个 POST 类型的接口 upload1 ,用于处理用户的上传请求,逻辑代码如下

// 入口文件 main.js

app.post('/upload1', multer().single('file'), (req, res) => {
// 1. 如果未传递文件,则什么也不做
if (!req.file) return res.send({ state: false, msg: '未上传文件' });

// 2. 得到文件名和二进制数据
let { originalname, buffer } = req.file;

// 3. 设置保存文件的完整路径
let saveFilePath = `./upload/${originalname}`;

// 4. 假如该文件已经存在,则不再写入,直接返回成功
if (fs.existsSync(saveFilePath)) {
return res.send({ state: true, msg: '上传成功' });
};

// 5. 写入文件,文件保存在upload文件夹内
fs.writeFile(saveFilePath, buffer, err => {
if(err) {
console.log(err);
res.send({ state: false, msg: '上传失败' });
}
res.send({ state: true, msg: '上传成功' });
});
})

可以看到我们使用了 multer 这个中间件,single的参数是我们前台配置的key,也就是 formdata.append(‘file’, data) 这里的 ‘file’。

它们两的名字必须要一样

重启node服务器,然后刷新浏览器,就可以通过点击上传按钮,选择任意一个文件进行上传了。


Base64

除了使用 formdata 方式提交文件外,我们还可以使用 base64 数据格式进行上传。

base64 方式 只适合小文件 传输,不建议上传大文件时使用,因为 base64 数据的体积比较大,而且编码也比较久。

大文件编码可能会导致内存不足,进而浏览器崩溃。

1. 绑定点击事件

打开 index.html ,为我们的按钮绑定点击事件,事件函数内编写我们的 Ajax 代码

// pages/index.html

// 为按钮绑定点击事件
btn.addEventListener('click', () => {
// 1. 创建 input 标签
let fileInput = document.createElement('input');

// 2. 设置type为文件类型
fileInput.type = 'file';

// 3. 触发file控件的点击事件
fileInput.click();

// 4. 为input绑定change事件,当用户选择好文件后,我们可以得到file对象
fileInput.addEventListener('change', function () {
// 5. 判断是否存在文件
if (!this.files.length) return;

// 6. 取出 file 对象
let file = this.files[0];

// 7. 调用base64上传
sendBase64(file);
})
})

// 我们的上传函数
function sendBase64(file) {
// 1. 得到 fileReader 实例对象
let fileReader = new FileReader();

// 2. 通过实例对象将我们的 file 对象读取为 base64
fileReader.readAsDataURL(file);

// 3. FileReader是异步的,onload代表读取完毕
fileReader.onload = e => {
let xhr = new XMLHttpRequest();

xhr.open('post', '/upload2');

// 4. 采用base64数据发送请求时,必须设置这个请求头,否则后台无法解析
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');

// 5. 配置我们要发送的数据,通过dataStringify进行序列化
// file是base64数据,filename是我们的文件名
let data = dataStringify({
// 6. 要用encodeURIComponent对base64编码,防止传输过程中出现乱码
file: encodeURIComponent(e.target.result),
filename: file.name
});

// 7. 将我们的data发送至后台
xhr.send(data);

xhr.onload = function () {
let res = JSON.parse(this.response);
if (res.state === true) {
alert('上传成功');
} else {
alert('上传失败');
}
}
}
}

// 自己实现的序列化函数,可以使用第三方库“qs”进行序列化
function dataStringify(obj) {
let str = [];
for (let c in obj) {
str.push(encodeURIComponent(c) + "=" + encodeURIComponent(obj[c]));
}
// 格式:xxx=xxx&yyy=yyy
return str.join("&");
}

注意点:

  1. 必须设置请求头的 Content-Type
  2. 必须采用 encodeURIComponent 进行编码
  3. 必须对请求体进行序列化

2. 添加后台接口

打开 main.js ,添加一个 POST 类型的接口 upload2 ,用于处理用户的上传请求,逻辑代码如下

// 入口文件 main.js

app.post('/upload2', (req, res) => {

// 1. 得到前端传过来的base64对象和文件名
let { file, filename } = req.body;

// 2. 设置保存文件的完整路径
let saveFilePath = `./upload/${filename}`;

// 3. 假如该文件已经存在,则不再写入,直接返回成功
if (fs.existsSync(saveFilePath)) {
return res.send({ state: true, msg: '切片接收完成' });
};

// 2. 对得到的base64数据进行解码
file = decodeURIComponent(file);

// 3. 删除base64的头信息
file = file.replace(/^data:.+;base64,/i, '');

// 4. 转换为 Buffer
file = Buffer.from(file, 'base64');

// 5. 写入文件,文件保存在upload文件夹内
fs.writeFile(saveFilePath, file, err => {
if(err) {
console.log(err);
return res.send({ state: false, msg: '上传失败' });
}
res.send({ state: true, msg: '上传成功' });
});
})

重启node服务器,然后刷新浏览器,就可以通过点击上传按钮,选择任意一个文件进行上传了。


并行上传

并行上传,其实就是将大文件按字节进行切割,切割成一个个小片段,然后在同一时间内,一次性发送多个请求给后台,每个请求中均携带着一个小片段,而这也叫做切片上传。

切片非常适用于 大文件的上传 ,以这种方式上传大体积的文件时,效率会很高,同时它的上传速度也比较快。

在开始写我们代码之前,有必要了解下并发上传的特点,所谓 并行,也可以称为 并发,它们的特点是在同一时间点内,同时执行N个相同的操作,例如在1s内同时发送50个请求。

并发是不保证顺序的,就好比田径场赛跑,多个选手同时起跑,谁先到达终点,谁最后到达,是没有一个固定顺序的,而场上的选手,就相当于我们的每一个请求,同时发送给后台,但谁先到达,谁先被后台处理,是不一定的。

理解完上面的概念之后,我们就可以开始编写我们的代码了。

文件切片

计算机内的任何文件其实都是以二进制数据的形式存在的,对一个文件进行切割,其实也就是对这二进制数据进行分割,将一个很长的二进制,分割成N个较短的二进制,在前端中,我们可以借助 FileReader 来帮我们完成这个需求

假如你对 FileReader 很陌生,那么建议你自行去了解一下它的介绍和用法,这里不会对它将的太多。

前端步骤:

  1. 绑定点击事件
  2. 将得到的file对象进行切片,将一个个切片装进一个请求队列中
  3. 使用 Promise.all 并发请求
  4. 在 then 内再发送merge请求合并切片(能进入then代表后台已成功接收所有切片)
  5. 判断 merge 的后台响应参数是成功还是失败

1. 绑定点击事件

打开 index.html ,为我们的按钮绑定点击事件,事件函数内编写我们的 Ajax 代码

// pages/index.html

btn.addEventListener('click', () => {
let fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.click();
fileInput.addEventListener('change', function () {
if (!this.files.length) return;
let file = this.files[0];
// 调用切片函数,得到我们的请求队列
let requestList = createRequestList(file, 100);
// 调用发送切片的函数
sendSlice(requestList, file);
})
})

然后,编写我们的 createRequestList 函数,这个函数的作用就是并行上传我们的文件

// pages/index.html

// 创建请求队列的函数
function createRequestList(file, sliceLength) {
// 1. 配置切片的个数,不传递或者传递的值不大于1,则默认切割为100份
if(!sliceLength || sliceLength <=1) {
sliceLength = 100;
}

// 2. 得到每一个切片的大小 (总大小/个数)
let size = file.size / sliceLength;

// 3. 请求队列,保存每一个请求
let requestList = [];

// 4. 当前切片开始切割的位置
let currentSize = 0;

// 5. 通过正则match提取出我们的文件名和后缀
let [, chunkFileName, suffix] = file.name.match(/(.+)(\.\w+)$/);

// 6. 通过循环100次,对我们的文件进行切割
for (let i = 0; i < sliceLength; i++) {

// 7. 配置我们的请求函数
let requestFn = new Promise((resolve, reject) => {

// 8. 要发送给后台的数据
let conf = {
// chunk是切片的二进制数据
chunk: file.slice(currentSize, currentSize + size),
// filename是切片文件名,格式=> 文件名_N.后缀
filename: `${chunkFileName}_${i}${suffix}`
}

// 9. 配置formdata
let formdata = new FormData();
formdata.append('chunk', conf.chunk);
formdata.append('filename', conf.filename);

// 10. 配置Ajax
let xhr = new XMLHttpRequest();
xhr.open('post', '/upload3');

// 11. 发送请求
xhr.send(formdata);

// 12. 监听请求状态
xhr.onload = () => {

// 13. 得到后台的响应参数
let res = JSON.parse(xhr.response);

// 判断当前切片是否被后台接收
if (res.state === true) {
resolve(res);
} else {
reject(res);
}

};
})

// 14. 将这个请求函数,添加到我们的请求队列中
requestList.push(requestFn);

// 15. 设置下一个切片的起始位置
currentSize += size;
}
// 16. 返回这个请求队列,当前请求队列里有100个请求函数
return requestList;
}

再然后,编写我们的 sendSlice 函数,这个函数的作用就是并行上传我们的文件

// pages/index.html

function sendSlice (list, file) {
// 通过 Promise.all 进行并发请求
Promise.all(list).then(() => {
// 全部切片上传成功后,请求合并切片
merge(file.name);
}).catch(err => {
console.error(err);
alert('上传失败');
});
}

最后,编写我们的 merge 函数,这个函数的作用是告诉后台我们的切片已经全部上传完毕,请求后台将那些切片进行合并

// pages/index.html

function merge() {
let xhr = new XMLHttpRequest();
xhr.open('get', `/merge?filename=${filename}`);
xhr.send();
xhr.onload = function () {
let res = JSON.parse(this.response);
if (res.state === true) {
alert('上传成功');
} else {
alert('上传失败');
}
}
}

接收合并

前端将一个个的切片进行上传后,我们的后台也要去接收它

后端步骤:

  1. 接收前端发送过来的切片,并保存至某个文件夹内
  2. 当接收到merge请求,则将这些切片进行合并成一个完整的文件
  3. 删除已保存的切片以及它们所在的文件夹

打开入口文件,新增 upload3 接口

// 入口文件main.js

// 切片上传
app.post('/upload3', multer().single('chunk'), (req, res) => {

// 1. 获取文件名(带索引和后缀),如demo_66.mp4
let { filename } = req.body;

// 2. 切片文件名(不带索引和后缀),如demo
let chunkName;

// 3. 原文件名(不带索引)
let originFileName;

// 4. 提取并设置切片文件的名称部分
try { // try-catch是为了防止前端传递的文件名错误
let matchFileName = filename.match(/^(.+)_\d+(\.\w+)/);

chunkName = matchFileName[1];

originFileName = matchFileName[1] + matchFileName[2];
} catch (error) {
return res.send({ state: false, msg: '文件名错误' });
}

// 5. 判断合并后的文件是否已经存在
if(fs.existsSync(`./upload/${originFileName}`)) {
return res.send({ state: true, msg: '切片接收完成' });
}

// 6. 定义切片文件存放的路径
// 假设切片文件名是 demo_1.mp4,则将demo开头的切片都保存至upload/demo/
let chunkPath = `./upload/${chunkName}`;

// 7. 判断保存切片的文件夹是否存在,如果不存在则创建
if (!fs.existsSync(chunkPath)) {
// 8. 文件夹不存在,创建该文件夹
fs.mkdirSync(chunkPath);
};

// 9. 切片文件的完整路径
let chunkFullPath = path.resolve(__dirname, chunkPath, filename);

// 10. 假如分片已存在,则不再写入,直接返回成功
if (fs.existsSync(chunkFullPath)) {
return res.send({ state: true, msg: '切片接收完成' });
};

// 11. 写入文件
fs.writeFile(chunkFullPath, req.file.buffer, (err) => {
if (err) throw err;
res.send({ state: true, msg: '切片接收完成' });
});
})

仍然是在main.js内,再新增一个 merge 接口,用于合并切片

// 入口文件main.js

// 1. 合并切片
app.get('/merge', (req, res) => {

// 2. 获取要合并的文件名
// 这里的文件名是完整的文件名,如demo.mp4
let { filename } = req.query;

// 3. 切片文件存放的路径,提取出文件名(不带后缀),得到如./upload/demo
let chunkPath = `./upload/${filename.match(/^(.+)(\.\w+)/)[1]}`;

// 4. 设置合并后的文件保存位置
let saveFilePath = `./upload/${filename}`;

// 5. 判断合并后的文件是否存在,如果存在,则直接返回成功
if(fs.existsSync(saveFilePath)) return res.send({ state: true, msg: '上传成功' });

// 6. 判断路径是否存在
if (!fs.existsSync(chunkPath)) return res.send({ state: false, msg: '不存在该文件' });

// 7. 读取目录下的所有文件,这个目录下就存放着要合并的所有切片
let dirs = fs.readdirSync(chunkPath);

// 8. 对切片文件进行顺序排列
dirs.sort((a, b) => {

// 9. 提取切片索引的正则表达式,如demo_66.mp4 => 66
let fileIndexRegExp = /_(\d+)/;

// 10. 提取出切片的索引进行比较
return fileIndexRegExp.exec(a)[1] - fileIndexRegExp.exec(b)[1]
});

try {
// 11. 遍历所有文件,并进行合并
for (let i = 0; i < dirs.length; i++) {

// 12. 遍历得到的每一个文件的路径
let file = dirs[i];

// 13. 得到切片文件的完整路径
let chunkFullPath = path.resolve(__dirname, chunkPath, file);

// 14. 写入文件
fs.appendFileSync(saveFilePath, fs.readFileSync(chunkFullPath));

// 15. 当一个切片写入完成后,则删除该切片
fs.unlinkSync(chunkFullPath);

}

// 16. 删除保存切片的文件夹
fs.rmdirSync(chunkPath);

res.send({ state: true, msg: '上传成功' });

} catch (error) {
console.log(error);
return res.send({ state: false, msg: '合并失败' });
}
})

到此,我们的大文件并发上传就完成了


完整代码

前端


<!-- pages/index.html -->
<!DOCTYPE html>
<html>

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>

<body>
<button id="btn">上传</button>

<script>
let btn = document.getElementById('btn');
btn.addEventListener('click', () => {
let fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.click();
fileInput.addEventListener('change', function () {
if (!this.files.length) return;

let file = this.files[0];

// 1. formdata方式
// sendFormData(file)

// 2. base64方式
// sendBase64(file)

// 3. 并发方式
/* let requestList = createRequestList(file, 100);
sendSlice(requestList, file); */

})
})


function sendFormData(data) {
let xhr = new XMLHttpRequest()
let formdata = new FormData()
formdata.append('file', data)
xhr.open('post', '/upload1')
console.time();
xhr.send(formdata)

xhr.onload = function () {
console.timeEnd();
let res = JSON.parse(this.response);
if (res.state === true) {
alert('上传成功')
} else {
alert('上传失败')
}
}
}


function sendBase64(file) {
let fileReader = new FileReader()

fileReader.readAsDataURL(file)

fileReader.onload = e => {
let xhr = new XMLHttpRequest()

xhr.open('post', '/upload2')
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')

let data = dataStringify({
file: encodeURIComponent(e.target.result),
filename: file.name
})

xhr.send(data)
xhr.onload = function () {
console.log(this);
}
}
}


function dataStringify(obj) {
let str = [];
for (let c in obj) {
str.push(encodeURIComponent(c) + "=" + encodeURIComponent(obj[c]));
}
return str.join("&");
}

// --------------------------
function sendSlice(list, file) {
// 并发上传
Promise.all(list).then(() => {
// 全部切片上传成功后,请求合并切片
merge(file.name);
}).catch(err => {
console.error(err);
alert('上传失败');
});
}


function createRequestList(file, sliceLength) {
if (!sliceLength || sliceLength <= 1) {
sliceLength = 100;
}
let size = file.size / sliceLength;
let requestList = [];
let currentSize = 0;
let [, chunkFileName, suffix] = file.name.match(/(.+)(\.\w+)$/);

for (let i = 0; i < sliceLength; i++) {
requestList.push(new Promise((resolve, reject) => {
let conf = {
chunk: file.slice(currentSize, currentSize + size),
filename: `${chunkFileName}_${i}${suffix}`
}

let form = new FormData();
form.append('chunk', conf.chunk);
form.append('filename', conf.filename);

let xhr = new XMLHttpRequest()
xhr.open('post', '/upload3')
xhr.send(form);

xhr.onload = () => {
let res = JSON.parse(xhr.response);
if (res.state === true) {
resolve(res);
} else {
reject(res);
}
};
}));

currentSize += size;
}
return requestList;
}

// 合并
function merge(filename) {
let xhr = new XMLHttpRequest();
xhr.open('get', `/merge?filename=${filename}`);
xhr.send();
xhr.onload = function () {
let res = JSON.parse(this.response);
if (res.state === true) {
alert('上传成功')
} else {
alert('上传失败')
}
}
}

</script>
</body>

</html>

后台

// 入口文件 main.js
const express = require('express')
const bodyParser = require('body-parser')
const path = require('path')
const fs = require('fs')
const multer = require('multer')
const app = express()

app.use(express.static(path.resolve(__dirname, 'pages')))

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false, limit: '5000mb', parameterLimit: 500000 }))

// formdata提交
app.post('/upload1', multer().single('file'), (req, res) => {

if (!req.file) return res.send({ state: false, msg: '未上传文件' });

let { originalname, buffer } = req.file;

let saveFilePath = `./upload/${originalname}`;

if (fs.existsSync(saveFilePath)) {
return res.send({ state: true, msg: '上传成功' });
};

fs.writeFile(saveFilePath, buffer, err => {
if (err) {
console.log(err);
res.send({ state: false, msg: '上传失败' });
}
res.send({ state: true, msg: '上传成功' });
});
})


// base64提交
app.post('/upload2', (req, res) => {

// 得到base64对象和文件名
let { file, filename } = req.body;

let saveFilePath = `./upload/${filename}`;

if (fs.existsSync(saveFilePath)) {
return res.send({ state: true, msg: '切片接收完成' });
};

file = decodeURIComponent(file);

file = file.replace(/^data:.+;base64,/i, '');

file = Buffer.from(file, 'base64');

fs.writeFile(saveFilePath, file, err => {
if (err) {
console.log(err);
res.send({ state: false, msg: '上传失败' });
}
res.send({ state: true, msg: '上传成功' });
});
})


// 分片上传
app.post('/upload3', multer().single('chunk'), (req, res) => {
// 获取文件名
let { filename } = req.body;
let chunkName;
let originFileName;

// 提取出文件名称部分
try {
let matchFileName = filename.match(/^(.+)_\d+(\.\w+)/);
chunkName = matchFileName[1];
originFileName = matchFileName[1] + matchFileName[2];
} catch (error) {
return res.send({ state: false, msg: '文件名错误' });
}

// 判断合并后的文件是否已经存在
if(fs.existsSync(`./upload/${originFileName}`)) {
return res.send({ state: true, msg: '切片接收完成' });
}

// 定义切片文件存放的路径
let chunkPath = `./upload/${chunkName}`;


// 判断保存切片的文件夹是否存在,如果不存在则创建
if (!fs.existsSync(chunkPath)) {
fs.mkdirSync(chunkPath);
};

// 完整路径
let chunkFullPath = path.resolve(__dirname, chunkPath, filename);

// 假如分片已存在,则不再写入,直接返回成功
if (fs.existsSync(chunkFullPath)) {
return res.send({ state: true, msg: '切片接收完成' });
};


// 写入文件
fs.writeFile(chunkFullPath, req.file.buffer, (err) => {
if (err) throw err;
res.send({ state: true, msg: '切片接收完成' });
});
})


// 合并
app.get('/merge', (req, res) => {

// 获取要合并的文件名
let { filename } = req.query;

// 切片文件存放的路径
let chunkPath = `./upload/${filename.match(/^(.+)(\.\w+)/)[1]}`;

// 设置完整文件的保存位置
let saveFilePath = `./upload/${filename}`;

// 判断完整的文件是否存在
if (fs.existsSync(saveFilePath)) return res.send({ state: true, msg: '上传成功' });

// 判断路径是否存在
if (!fs.existsSync(chunkPath)) return res.send({ state: false, msg: '不存在该文件' });

// 读取目录下的所有文件
let dirs = fs.readdirSync(chunkPath);

// 对切片文件进行顺序排列
dirs.sort((a, b) => {
let fileIndexRegExp = /_(\d+)/;
// 提取出切片的索引进行比较
return fileIndexRegExp.exec(a)[1] - fileIndexRegExp.exec(b)[1]
});

try {
// 遍历所有文件,并进行合并
for (let i = 0; i < dirs.length; i++) {
let file = dirs[i];
// 得到切片文件的完整路径
let chunkFullPath = path.resolve(__dirname, chunkPath, file);
// 写入文件
fs.appendFileSync(saveFilePath, fs.readFileSync(chunkFullPath));
// 当一个切片写入完成后,则删除该切片
fs.unlinkSync(chunkFullPath);
}

// 删除保存切片的文件夹
fs.rmdirSync(chunkPath);

res.send({ state: true, msg: '上传成功' });

} catch (error) {
console.log(error);
return res.send({ state: false, msg: '合并失败' });
}
})


app.listen(8080, () => {
console.log('http://127.0.0.1:8080');
})
-------------本文结束    感谢阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!