大文件分片上传前端框架_基于Node.js的大文件分片上传
基于Node.js的大文件分片上传
我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况。所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作。同时如果文件过大,在网络不佳的情况下,如何做到断点续传?也是需要记录当前上传文件,然后在下一次进行上传请求的时候去做判断。
前端
1. index.html
文件上传
$(document).ready(() => {
const chunkSize = 1 * 1024 * 1024; // 每个chunk的大小,设置为1兆
// 使用Blob.slice方法来对文件进行分割。
// 同时该方法在不同的浏览器使用方式不同。
const blobSlice =
File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
const hashFile = (file) => {
return new Promise((resolve, reject) => {
const chunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
const spark = new SparkMD5.ArrayBuffer();
const fileReader = new FileReader();
function loadNext() {
const start = currentChunk * chunkSize;
const end = start + chunkSize >= file.size ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
}
fileReader.onload = e => {
spark.append(e.target.result); // Append array buffer
currentChunk += 1;
if (currentChunk < chunks) {
loadNext();
} else {
console.log('finished loading');
const result = spark.end();
// 如果单纯的使用result 作为hash值的时候, 如果文件内容相同,而名称不同的时候
// 想保留两个文件无法保留。所以把文件名称加上。
const sparkMd5 = new SparkMD5();
sparkMd5.append(result);
sparkMd5.append(file.name);
const hexHash = sparkMd5.end();
resolve(hexHash);
}
};
fileReader.onerror = () => {
console.warn('文件读取失败!');
};
loadNext();
}).catch(err => {
console.log(err);
});
}
const submitBtn = $('#submitBtn');
submitBtn.on('click', async () => {
const fileDom = $('#file')[0];
// 获取到的files为一个File对象数组,如果允许多选的时候,文件为多个
const files = fileDom.files;
const file = files[0];
if (!file) {
alert('没有获取文件');
return;
}
const blockCount = Math.ceil(file.size / chunkSize); // 分片总数
const axiosPromiseArray = []; // axiosPromise数组
const hash = await hashFile(file); //文件 hash
// 获取文件hash之后,如果需要做断点续传,可以根据hash值去后台进行校验。
// 看看是否已经上传过该文件,并且是否已经传送完成以及已经上传的切片。
console.log(hash);
for (let i = 0; i < blockCount; i++) {
const start = i * chunkSize;
const end = Math.min(file.size, start + chunkSize);
// 构建表单
const form = new FormData();
form.append('file', blobSlice.call(file, start, end));
form.append('name', file.name);
form.append('total', blockCount);
form.append('index', i);
form.append('size', file.size);
form.append('hash', hash);
// ajax提交 分片,此时 content-type 为 multipart/form-data
const axiosOptions = {
onUploadProgress: e => {
// 处理上传的进度
console.log(blockCount, i, e, file);
},
};
// 加入到 Promise 数组中
axiosPromiseArray.push(axios.post('/file/upload', form, axiosOptions));
}
// 所有分片上传后,请求合并分片文件
await axios.all(axiosPromiseArray).then(() => {
// 合并chunks
const data = {
size: file.size,
name: file.name,
total: blockCount,
hash
};
axios
.post('/file/merge_chunks', data)
.then(res => {
console.log('上传成功');
console.log(res.data, file);
alert('上传成功');
})
.catch(err => {
console.log(err);
});
});
});
})
window.onload = () => {
}
大文件上传测试
自定义上传文件
2. 依赖的文件
后端
1. app.js
const Koa = require('koa');
const app = new Koa();
const Router = require('koa-router');
const multer = require('koa-multer');
const serve = require('koa-static');
const path = require('path');
const fs = require('fs-extra');
const koaBody = require('koa-body');
const { mkdirsSync } = require('./utils/dir');
const uploadPath = path.join(__dirname, 'uploads');
const uploadTempPath = path.join(uploadPath, 'temp');
const upload = multer({ dest: uploadTempPath });
const router = new Router();
app.use(koaBody());
/**
* single(fieldname)
* Accept a single file with the name fieldname. The single file will be stored in req.file.
*/
router.post('/file/upload', upload.single('file'), async (ctx, next) => {
console.log('file upload...')
// 根据文件hash创建文件夹,把默认上传的文件移动当前hash文件夹下。方便后续文件合并。
const {
name,
total,
index,
size,
hash
} = ctx.req.body;
const chunksPath = path.join(uploadPath, hash, '/');
if(!fs.existsSync(chunksPath)) mkdirsSync(chunksPath);
fs.renameSync(ctx.req.file.path, chunksPath + hash + '-' + index);
ctx.status = 200;
ctx.res.end('Success');
})
router.post('/file/merge_chunks', async (ctx, next) => {
const {
size, name, total, hash
} = ctx.request.body;
// 根据hash值,获取分片文件。
// 创建存储文件
// 合并
const chunksPath = path.join(uploadPath, hash, '/');
const filePath = path.join(uploadPath, name);
// 读取所有的chunks 文件名存放在数组中
const chunks = fs.readdirSync(chunksPath);
// 创建存储文件
fs.writeFileSync(filePath, '');
if(chunks.length !== total || chunks.length === 0) {
ctx.status = 200;
ctx.res.end('切片文件数量不符合');
return;
}
for (let i = 0; i < total; i++) {
// 追加写入到文件中
fs.appendFileSync(filePath, fs.readFileSync(chunksPath + hash + '-' +i));
// 删除本次使用的chunk
fs.unlinkSync(chunksPath + hash + '-' +i);
}
fs.rmdirSync(chunksPath);
// 文件合并成功,可以把文件信息进行入库。
ctx.status = 200;
ctx.res.end('合并成功');
})
app.use(router.routes());
app.use(router.allowedMethods());
app.use(serve(__dirname + '/static'));
app.listen(9000);
2. utils/dir.js
const path = require('path');
const fs = require('fs-extra');
const mkdirsSync = (dirname) => {
if(fs.existsSync(dirname)) {
return true;
} else {
if (mkdirsSync(path.dirname(dirname))) {
fs.mkdirSync(dirname);
return true;
}
}
}
module.exports = {
mkdirsSync
};
操作步骤说明
服务端的搭建
我们以下的操作都是保证在已经安装node以及npm的前提下进行。node的安装以及使用可以参考
官方网站。
新建项目文件夹file-upload
使用npm初始化一个项目:cd file-upload && npm init
安装相关依赖
npm i koa
npm i koa-router --save // Koa路由
npm i koa-multer --save // 文件上传处理模块
npm i koa-static --save // Koa静态资源处理模块
npm i fs-extra --save // 文件处理
npm i koa-body --save // 请求参数解析
创建项目结构
file-upload
- static
- index.html
- spark-md5.min.js
- uploads
- temp
- utils
- dir.js
- app.js
复制相应的代码到指定位置即可
项目启动:node app.js (可以使用 nodemon 来对服务进行管理)
访问:http://localhost:9000/index.html
其中细节部分代码里有相应的注释说明,浏览代码就一目了然。
后续延伸:断点续传、多文件多批次上传
作者:饭等米
大文件分片上传前端框架_基于Node.js的大文件分片上传相关推荐
- js获取request中的值_基于node.js的开发框架 — Koa
一.简介 Koa 基于nodeJs平台的下一代web开发框架,由 Express 幕后的原班人马打造,致力于成为一个更小.更富有表现力.更健壮的 Web 框架.使用 koa 编写 web 应用,通过组 ...
- 大文件分片上传前端框架_无插件实现大文件分片上传,断点续传
文件上传.gif 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试上传等需求的使用场景及实现: 2. 项目需求 在一个音视频的添加中,既要有音视频 ...
- 基于vue前端框架_基于前端访问控制框架的Vue
基于vue前端框架 权限访问控制 (vue-access-control) Vue-Access-Control is a solution of front-end user rights cont ...
- node.js web框架_使用Node.js进行Web爬取的终极指南
node.js web框架 So what's web scraping anyway? It involves automating away the laborious task of colle ...
- 基于vue的响应式ui框架_基于Vue.js的响应式和可配置UI框架
基于vue的响应式ui框架 Framevuerk (Framevuerk) Fast, Responsive, Multi Language, Both Direction Support and C ...
- 在Ubuntu 14.04上设置生产环境可用的Node.js
在Ubuntu 14.04上设置生产环境可用的Node.js 提供:ZStack社区 前言 Node.js是一个开源的JavaScript运行时环境,开发者可以用它方便的构建服务器端应用和网络应用.N ...
- js node.js读取excel文件返回为json文本
node-xlsx: 基于Node.js解析excel文件数据及生成excel文件:只支持xlsx xlsx: 基于Node.js解析excel文件数据及生成excel文件:只支持xlsx excel ...
- 前端框架vue3的node安装及项目构建的4种方法
前端框架vue3的node安装及项目构建的4种方法 C:\Users\Mac\Documents\newlifewyq\技术精英-source\vue\vue3pro>cnpm install ...
- Node.js 在大前端领域的应用分析
作者:前端361 原文地址:https://zhuanlan.zhihu.com/p/121055042 关于 node 的使用已经很久了,使用范围也很广,似乎有前端的地方就有 node,那么来思考一 ...
最新文章
- 手机耗电统计app_Android O新特性:精确统计APP电量消耗
- node-inspect命令行工具的调试使用方法
- csv.writer写入文件有多余的空行
- Kotlin之?和!!最简单的理解
- codevs4343 找回密码
- 每日一题(53)—— 评价代码片段
- ubuntun 16.04环境安装Caffe过程
- 如何修改计算机无线mac地址,修改计算机mac地址_怎么修改mac地址
- Python自然语言处理学习笔记(41):5.2 标注语料库
- ​asp家教交流平台系统设计网站作品
- mysql 连接 监控_监控mysql上客户端的连接数
- 国外10个优秀音乐网站推荐
- 5、ORB-SLAM闭环检测之通过求解出来的sim3寻找当前关键帧和闭环候选帧之间的更多匹配
- 2021基于vscode以及jlink调试esp32最新
- sap 消耗策略999_SAP 计划策略
- linux与windows内核哪个难学,国产操作系统为何都基于Linux内核?又和Windows像?
- 100G网络升级的路径以及布线方式有哪些?
- 提前揭秘CJ八大看点
- Zabbix一键部署
- 汉字logo就是土?你怕是没见识过我们中国的这些标志
热门文章
- SAP Spartacus 如何使用 API 从浏览器 local Storage 读取数据
- SAP 电商云 Spartacus UI 从 CMS 取回 slots 和 component 之后的处理
- SAP Spartacus API 的事务处理特性
- SAP Spartacus user和org user form两处不同的checkbox风格
- rxjs里的Observable对象和map配合的一个用法
- 使用SAP OData offline库实现Android应用的离线(offline)模式
- 如何使用SAP UI5 web Component的React框架的柱状图和折线图
- OPA 1 - testsuite.opa.html
- oModel.create will also send to backend directly
- SAP CRM settype的传输原理