【NestJS】资源的上传和下载

上传资源

multer 是一个基于 Express 的中间件,用于处理 multipart/form-data 格式的数据,主要用于上传文件。
NestJS 内置了 multer,可以使用 @nestjs/platform-express 包中导出的 FileInterceptorFilesInterceptor 等拦截器来使用 multer 的功能。

  1. npm i multernpm i @types/multer -D
  2. nest g res user --no-spec
  3. 配置 module 文件
import {
    
     Module } from '@nestjs/common';
import {
    
     UserController } from './user.controller';
import {
    
     MulterModule } from '@nestjs/platform-express';
import {
    
     diskStorage } from 'multer';
import {
    
     extname, join } from 'path';

// 配置文件上传
const multerOptions: MulterOptions = {
    
    
    // 配置文件的存储
    storage: diskStorage({
    
    
        // 存储地址
        destination: join(__dirname, '../../../public/notification-attachment'),
        // 存储名称
        filename: (_req, file, callback) => {
    
    
            const suffix = extname(file.originalname); // 获取文件后缀
            const docName = new Date().getTime(); // 自定义文件名
            return callback(null, `${
      
      docName}${
      
      suffix}`);
        },
    }),
    // 过滤存储的文件
    fileFilter: (_req, file, callback) => {
    
    
        // multer 默认使用 latin1 编码来解析文件名, 而 latin1 编码不支持中文字符, 所以会出现中文名乱码的现象
        // 这里将文件名从 latin1 编码转换为 Buffer 对象, 再用 toString('utf8') 将 Buffer 对象转换为 utf8 编码的字符串
        // utf8 是一种支持多国语言的编码方式, 这样就可以保证文件名的中文字符不会被错误解析
        file.originalname = Buffer.from(file.originalname, 'latin1').toString(
            'utf8',
        );
        callback(null, true);
    },
    // 限制文件大小
    limits: {
    
    
        // 限制文件大小为 10 MB
        fileSize: 10 * 1024 * 1024, // 默认无限制
        // 限制文件名长度为 50 bytes
        fieldNameSize: 50, // 默认 100 bytes
    },
};

@Module({
    
    
    imports: [
        MulterModule.register(multerOptions),
        // 如需异步配置, 可使用 MulterModule.registerAsync
    ],
    controllers: [UserController],
})
export class UserModule {
    
    }
  1. 配置 controller 文件

上传单个文件时,使用拦截器 FileInterceptor('参数名')FileInterceptor 会将上传的文件注入到控制器方法的参数中,你可以用 @UploadedFile() 装饰器来获取它

import {
    
    
    Controller,
    Post,
    UseInterceptors,
    UploadedFile,
} from '@nestjs/common';
import {
    
     FileInterceptor } from '@nestjs/platform-express/multer';

@Controller('user')
export class UserController {
    
    
    @Post('album')
    @UseInterceptors(FileInterceptor('picture')) // 使用拦截器 FileInterceptor
    upload(@UploadedFile() file: Express.Multer.File /* 获取上传的文件 */ ) {
    
    
        return {
    
     file };
    }
}
  1. 使用 Apifox 模拟前端上传图片

前端 POST 请求携带的参数名要与后端拦截器 FileInterceptor('参数名') 中的 '参数名' 一样

image.png

  1. 在 dist 目录下就可以预览到前端上传的文件啦



访问已上传的资源

  • 配置静态资源路径
import {
    
     NestFactory } from '@nestjs/core';
import {
    
     NestExpressApplication } from '@nestjs/platform-express/interfaces';
import {
    
     join } from 'path';
import {
    
     AppModule } from './app.module';

async function bootstrap() {
    
    
    // 显示配置 NestExpressApplication 类型, 以获取更好的语法提示
    const app = await NestFactory.create<NestExpressApplication>(AppModule);

    app.useStaticAssets(join(__dirname, './images')); // 配置静态资源目录

    await app.listen(3000);
}
bootstrap();

到 dist 目录下复制上传的图片文件名 XXX,打开 http://127.0.0.1:3000/XXX 即可访问上传的图片

image.png


  • 配置静态资源虚拟路径
app.useStaticAssets(join(__dirname, './images'), {
    
    
    prefix: '/static', // 配置静态资源虚拟路径
});

现在需要打开 http://127.0.0.1:3000/static/XXX 才能访问上传的图片



下载资源

直接下载

使用 Express 的方法 res.download(path[, filename])
path 是一个字符串,表示文件的绝对或相对路径;
filename 是一个可选的字符串,表示建议的文件名,如果省略,则使用 path 的基本名称;

  1. 配置 controller 文件
import {
    
     Controller, Get, Res } from '@nestjs/common';
import {
    
     Response } from 'express';
import {
    
     join } from 'path';

@Controller('user')
export class UserController {
    
    
    @Get('album')
    getImg(@Res() res: Response) {
    
    
        const url = join(__dirname, '../images/1676791106510.png'); // 正常开发中 url 应该是从数据库中的
        res.download(url); // 将指定路径的文件作为附件传输给浏览器
    }
}

现在访问 http://127.0.0.1:3000/user/album 即可下载图片文件

  1. 配置前端下载:
<template>
    <el-button @click="download('/upload/export')">下载图片资源</el-button>
</template>

<script lang="ts" setup>
const download = (url: string) => {
      
      
    window.open(url);
};
</script>

文件流下载

compressing 用于压缩和解压缩文件和流,支持 gzip、deflate、zip、tar、tgz、tbz2 等格式。

  1. npm i compressing
  2. 配置 controller 文件
import {
    
     Controller, Get, Res } from '@nestjs/common';
import {
    
     zip } from 'compressing';
import {
    
     Response } from 'express';
import {
    
     join } from 'path';

@Controller('user')
export class UserController {
    
    
    @Get('photo')
    getImg(@Res() res: Response) {
    
    
        const url = join(__dirname, '../images/1676791106510.png'); // 正常开发中 url 应该是从数据库中的

        const targetStream = new zip.Stream(); // 创建一个可读的压缩流, 可以将任何数据压缩成 zip 格式
        targetStream.addEntry(url); // 向压缩流中添加文件

        res.setHeader('Content-Type', 'application/octet-stream'); // 设置文件格式为 '流'
        res.setHeader(
            'Content-Disposition', // 设置文件以什么方式呈现
            'attachment; filename=superman',
            // attachment 表示文件应该被下载到本地;    filename=superman 表示下载文件的文件名
        );

        targetStream.pipe(res); // 将压缩流输出给响应流
    }
}

现在访问 http://127.0.0.1:3000/user/photo 即可下载 “流” 文件

  1. 前端解析并下载 “流” 文件
<template>
    <el-button @click="downloadByStream('/upload/stream')">下载流文件</el-button>
</template>

<script lang="ts" setup>
const downloadByStream = async (url: string) => {
      
      
    const res = await fetch(url).then(res => res.arrayBuffer());
    // 注意: 前端接收的是流文件, 这里需要返回 res.arrayBuffer() 而不是 res.json()
    // 如果是使用 axios, 则需要设置 responseType 为 ArrayBuffer / Blob

    const blob = new Blob([res]); // 将流文件转成 blob 形式
    const imgUrl = URL.createObjectURL(blob); // 通过 blob 生成可访问的链接

    const a = document.createElement('a');
    a.href = imgUrl;
    a.download = 'superman.zip';
    a.click();
};
</script>

猜你喜欢

转载自blog.csdn.net/Superman_H/article/details/130171512