vue大文件切片上传、并发上传、断点续传与后端的逻辑(express 、nestJs)

Vue大文件分片上传可以通过将大文件进行切片,实现并发上传和断点续传,从而提高上传速度和用户体验。

1. 图方便直接原生,上传表单

<template>
  <form enctype="multipart/form-data">
    <input type="file" ref="fileInput" @change="onFileChange" />
    <button @click.prevent="upload">上传</button>
  </form>
</template>

2. 实现上传功能,主要包括文件切片、分片上传和断点续传

<script>
export default {
  methods: {
    onFileChange() {
      // 将大文件切片
      const file = this.$refs.fileInput.files[0]
      const maxChunkSize = 10 * 1024 * 1024 // 分片大小
      const chunks = Math.ceil(file.size / maxChunkSize)
      const chunkList = []
      for (let i = 0; i < chunks; i++) {
        const start = i * maxChunkSize
        const end = Math.min((i + 1) * maxChunkSize, file.size)
        chunkList.push(file.slice(start, end))
      }

      // 并发上传所有分片
      const promises = chunkList.map((chunk, index) => {
        const formData = new FormData()
        formData.append('index', index)
        formData.append('chunk', chunk)
        return this.uploadChunk(formData)
      })

      // 等待所有分片上传完成后进行合并
      Promise.all(promises).then(() => {
        this.mergeChunks(file.name, chunks)
      })
    },
    async uploadChunk(formData) {
      try {
        await this.$axios.post('/uploadChunk', formData)
      } catch (error) {
        console.log('分片上传失败:', error)
        throw error
      }
    },
    async mergeChunks(filename, chunks) {
      try {
        await this.$axios.post('/mergeChunks', { filename, chunks })
        console.log('上传成功!')
      } catch (error) {
        console.log('合并分片失败:', error)
        throw error
      }
    },
  },
}
</script>

3. 后端用的是 express 实现的分片上传接口和分片合并接口,主要包括对文件进行切片、上传分片到服务器和将上传完成的分片进行合并

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

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

// 实现分片上传接口
app.post('/uploadChunk', (req, res) => {
  const index = req.body.index
  const chunk = req.body.chunk
  const dir = path.join(__dirname, 'temp')
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir)
  }
  const tempFilePath = path.join(dir, index.toString())
  fs.writeFileSync(tempFilePath, chunk)
  res.status(200).send('分片上传成功!')
})

// 实现分片合并接口
app.post('/mergeChunks', (req, res) => {
  const filename = req.body.filename
  const chunks = parseInt(req.body.chunks)
  const dir = path.join(__dirname, 'temp')
  const filePath = path.join(__dirname, 'uploads', filename)

  // 根据分片序号读取对应文件,并将所有分片写入一个新的文件中
  const writeStream = fs.createWriteStream(filePath)
  for (let i = 0; i < chunks; i++) {
    const readStream = fs.createReadStream(path.join(dir, i.toString()))
    readStream.pipe(writeStream, { end: false })
    readStream.on('end', () => {
      fs.unlinkSync(path.join(dir, i.toString()))
    })
  }
  writeStream.on('close', () => {
    res.status(200).send('分片合并成功!')
  })
})

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

另外,我为了练手也用nestjs实现了一下大文件上传的后端逻辑

1. 安装以下依赖

npm install --save @nestjs/core @nestjs/platform-express body-parser cors express fs-extra

2. 然后在 app.module.ts 中配置依赖和中间件

import { Module, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MulterModule } from '@nestjs/platform-express';
import * as path from 'path';
import * as bodyParser from 'body-parser';
import * as cors from 'cors';
import * as express from 'express';
import * as fsExtra from 'fs-extra';

@Module({
  imports: [
    MulterModule.register({
      dest: path.join(__dirname, '..', 'uploads'),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(bodyParser.json(), bodyParser.urlencoded({ extended: true }), cors())
      .forRoutes('*');
    // 静态文件中间件
    const uploadPath = path.join(__dirname, '..', 'uploads');
    const tempPath = path.join(__dirname, '..', 'temp');
    const staticResourceMiddleware = express.static(uploadPath);
    const tempResourceMiddleware = express.static(tempPath);
    consumer
      .apply(staticResourceMiddleware)
      .forRoutes({ path: 'uploads/*', method: RequestMethod.GET });
    consumer
      .apply(tempResourceMiddleware)
      .forRoutes({ path: 'temp/*', method: RequestMethod.GET });
  }
}

3. 上面的代码中我引入了 MulterModulepathbody-parsercorsexpressfs-extra 等依赖,并使用 configure 方法在 NestJS 应用中配置了中间件。

接着,在 app.service.ts 文件中,实现上传和合并文件的业务逻辑

import { Injectable } from '@nestjs/common';
import * as path from 'path';
import * as fsExtra from 'fs-extra';

@Injectable()
export class AppService {
  async uploadFileChunk(file: any, body: any) {
    const index = body.index;
    const dir = path.join(__dirname, '..', 'temp');
    if (!fsExtra.existsSync(dir)) {
      fsExtra.mkdirSync(dir);
    }
    const tempFilePath = path.join(dir, index.toString());
    await fsExtra.writeFile(tempFilePath, file.buffer);

    return '分片上传成功!';
  }

  async mergeFile(filename: string, chunks: number) {
    const dir = path.join(__dirname, '..', 'temp');
    const writeStream = fsExtra.createWriteStream(path.join(__dirname, '..', 'uploads', filename));
    for (let i = 0; i < chunks; i++) {
      try {
        const readStream = fsExtra.createReadStream(path.join(dir, i.toString()));
        await new Promise((resolve, reject) => {
          readStream.pipe(writeStream, { end: false });
          readStream.on('end', async () => {
            await fsExtra.unlink(path.join(dir, i.toString()));
            resolve();
          });
          readStream.on('error', reject);
        });
      } catch (error) {
        console.log('合并分片失败:', error);
        throw error;
      }
    }
    return '上传成功!';
  }
}

4. 我将上传和合并文件的逻辑分别封装在了 uploadFileChunkmergeFile 方法中。

最后,在 app.controller.ts 中实现控制器逻辑,调用 AppService 中的方法

import { Controller, Post, UploadedFile, UseInterceptors, Body } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Post('upload')
  @UseInterceptors(FileInterceptor('file'))
  async upload(@UploadedFile() file, @Body() body) {
    return this.appService.uploadFileChunk(file, body);
  }

  @Post('merge')
  async merge(@Body() body) {
    const filename = body.filename;
    const chunks = parseInt(body.chunks);
    return this.appService.mergeFile(filename, chunks);
  }
}

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
一套目前来说最好的nestjs实战教程,提供QQ长期问答服务. 本人从 08 年到 18 年一直从事于 PHP 的开发。从 18 年开始转向 Typescript+React+Nestjs 的技术栈。目前来说 React 应该是一个非常好用的前端框架,生态非常完善,并且十分灵活简单。Nestjs 则是 Node.js 中唯一且无敌存在的后端 web 框架。因为我个人从事这套技术栈开发已经 4 年多,所以颇有心得,做了这套 React18 视频教程和 Nestjs 实战视频教程。现在视频教程也是刚刚开始做了一部分,还在持续更新。使用 TS 全栈开发可以基本涵盖各种平台的方方面面,比如开发桌面应用的 Electron, 开发小程序的 Taro, 开发 Spa 中后台的 React,开发 SSR 网站的 next.js,开发移动应用的 React Native, 开发 CLI 的 Yargs, 以及开发后端Nestjs。基本学会一套,全面够用,再加上 Monorepo 组织结构,一个仓库所有平台都可以搞定。 包含以下知识点 - 掌握Nestjs框架的依赖注入,模块,提供者,生命周期等概念- 掌握DTO数据验证,响应序列化,异常过滤器等常用功能- 学会编写一些常用的class-validator验证约束- 熟练掌握Typeorm以及Nestjs与Typeorm结合开发- 学会整合Swagger输出Open API文档- 掌握TS装饰器以及反射元数据的定义和使用- 编写一些数据库相关的数据验证约束(比如树形表的同级别某字段唯一验证等)- 学会通过继承并魔改Nestjs源码编写自定义的全局验证器- 可以编写自定义的配置系统以及核心功能包- 学会自定义的代码组织方式(比如教程中我把默认的Nestjs应用改成Util+PluginModule模式)- 掌握编写一些常用的Util仓库(比如数据库,Redis,Restful)- 利用Yargs结合魔改后的框架可以编写一些自定义CLI命令(比如数据迁移,数据填充等)- 掌握如何利用阿里云/腾讯云推送邮件和短信- 掌握使用消息列队(MQ)的方式异步推送邮件和短信- 掌握守卫原理以及编写一些用户验证的守卫- 编写一个完善的用户系统(JWT认证,短信/邮件登录,短信/邮件注册,找回密码,绑定手机和邮箱等)- 熟练地通过编写装饰器去实现一些常用的功能- 通过SSE,WebSockets实现用户的上线,下线以及消息实时推送,消息广播等- 学会使用云存储来上传文件- 学会大文件断点雪川- 实现RBAC的权限系统- 理解请求范围概念以及性能方便的考量- 自己构建配置系统,实现配置验证以及通过YAML或数据库来进行动态配置- 通过适用Vscode进行Debug以及编写Jest测试来提升开发效率与程序的可用性- 学会使用Node来编写自定义的CLI命令- 利用NestCURD进行快速开发- 学会Graphql替代Restful写API- 使用Mongodb替代关系型数据库- 掌握一些常用的打包工具,比如通过ncc打包成单文件,通过pack打包成二进制等- 学会一些常用的部署方式,比如通过nginx+pm2反向代理部署,devops自动化CI,CD等- 学会使用pnpm workspaces来使用monreopo组织代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值