基于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的大文件分片上传相关推荐

  1. js获取request中的值_基于node.js的开发框架 — Koa

    一.简介 Koa 基于nodeJs平台的下一代web开发框架,由 Express 幕后的原班人马打造,致力于成为一个更小.更富有表现力.更健壮的 Web 框架.使用 koa 编写 web 应用,通过组 ...

  2. 大文件分片上传前端框架_无插件实现大文件分片上传,断点续传

    文件上传.gif 1. 简介: 本篇文章基于实际项目的开发,将介绍项目中关于大文件分片上传.文件验证.断点续传.手动重试上传等需求的使用场景及实现: 2. 项目需求 在一个音视频的添加中,既要有音视频 ...

  3. 基于vue前端框架_基于前端访问控制框架的Vue

    基于vue前端框架 权限访问控制 (vue-access-control) Vue-Access-Control is a solution of front-end user rights cont ...

  4. node.js web框架_使用Node.js进行Web爬取的终极指南

    node.js web框架 So what's web scraping anyway? It involves automating away the laborious task of colle ...

  5. 基于vue的响应式ui框架_基于Vue.js的响应式和可配置UI框架

    基于vue的响应式ui框架 Framevuerk (Framevuerk) Fast, Responsive, Multi Language, Both Direction Support and C ...

  6. 在Ubuntu 14.04上设置生产环境可用的Node.js

    在Ubuntu 14.04上设置生产环境可用的Node.js 提供:ZStack社区 前言 Node.js是一个开源的JavaScript运行时环境,开发者可以用它方便的构建服务器端应用和网络应用.N ...

  7. js node.js读取excel文件返回为json文本

    node-xlsx: 基于Node.js解析excel文件数据及生成excel文件:只支持xlsx xlsx: 基于Node.js解析excel文件数据及生成excel文件:只支持xlsx excel ...

  8. 前端框架vue3的node安装及项目构建的4种方法

    前端框架vue3的node安装及项目构建的4种方法 C:\Users\Mac\Documents\newlifewyq\技术精英-source\vue\vue3pro>cnpm install ...

  9. Node.js 在大前端领域的应用分析

    作者:前端361 原文地址:https://zhuanlan.zhihu.com/p/121055042 关于 node 的使用已经很久了,使用范围也很广,似乎有前端的地方就有 node,那么来思考一 ...

最新文章

  1. 手机耗电统计app_Android O新特性:精确统计APP电量消耗
  2. node-inspect命令行工具的调试使用方法
  3. csv.writer写入文件有多余的空行
  4. Kotlin之?和!!最简单的理解
  5. codevs4343 找回密码
  6. 每日一题(53)—— 评价代码片段
  7. ubuntun 16.04环境安装Caffe过程
  8. 如何修改计算机无线mac地址,修改计算机mac地址_怎么修改mac地址
  9. Python自然语言处理学习笔记(41):5.2 标注语料库
  10. ​asp家教交流平台系统设计网站作品
  11. mysql 连接 监控_监控mysql上客户端的连接数
  12. 国外10个优秀音乐网站推荐
  13. 5、ORB-SLAM闭环检测之通过求解出来的sim3寻找当前关键帧和闭环候选帧之间的更多匹配
  14. 2021基于vscode以及jlink调试esp32最新
  15. sap 消耗策略999_SAP 计划策略
  16. linux与windows内核哪个难学,国产操作系统为何都基于Linux内核?又和Windows像?
  17. 100G网络升级的路径以及布线方式有哪些?
  18. 提前揭秘CJ八大看点
  19. Zabbix一键部署
  20. 汉字logo就是土?你怕是没见识过我们中国的这些标志

热门文章

  1. SAP Spartacus 如何使用 API 从浏览器 local Storage 读取数据
  2. SAP 电商云 Spartacus UI 从 CMS 取回 slots 和 component 之后的处理
  3. SAP Spartacus API 的事务处理特性
  4. SAP Spartacus user和org user form两处不同的checkbox风格
  5. rxjs里的Observable对象和map配合的一个用法
  6. 使用SAP OData offline库实现Android应用的离线(offline)模式
  7. 如何使用SAP UI5 web Component的React框架的柱状图和折线图
  8. OPA 1 - testsuite.opa.html
  9. oModel.create will also send to backend directly
  10. SAP CRM settype的传输原理