【NestJS】Resource upload and download

upload resources

multer is an Express-based middleware for processing data multipart/form-datain format , mainly for uploading files.
NestJS has a built-in multer, and you can use the interceptors exported in the @nestjs/platform-express package to use the multer function FileInterceptor.FilesInterceptor

  1. npm i multernpm i @types/multer -D
  2. nest g res user --no-spec
  3. configuration module file
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. Configure the controller file

When uploading a single file, use the interceptor FileInterceptor('参数名')to FileInterceptorinject the uploaded file into the parameters of the controller method, you can use @UploadedFile()the decorator to get it

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. Use Apifox to simulate front-end uploading pictures

The parameter name carried in the front-end POST request should be the same as that FileInterceptor('参数名')in the back-end interceptor'参数名'

image.png

  1. In the dist directory, you can preview the files uploaded by the front end.



Access uploaded resources

  • Configure static resource path
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();

Go to the dist directory and copy the uploaded image file name XXX, open http://127.0.0.1:3000/XXXto access the uploaded image

image.png


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

Now need to open http://127.0.0.1:3000/static/XXXto access uploaded images



download resources

Download

The method using Express res.download(path[, filename]):
pathis a string indicating the absolute or relative path of the file;
filenameis an optional string indicating the suggested file name, if omitted, the base name pathof ;

  1. Configure the controller file
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); // 将指定路径的文件作为附件传输给浏览器
    }
}

Now visit http://127.0.0.1:3000/user/album to download the picture file

  1. Configure front-end downloads:
<template>
    <el-button @click="download('/upload/export')">下载图片资源</el-button>
</template>

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

file stream download

compressing is used to compress and decompress files and streams, supporting formats such as gzip, deflate, zip, tar, tgz, tbz2, etc.

  1. npm i compressing
  2. Configure the controller file
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); // 将压缩流输出给响应流
    }
}

Now visit http://127.0.0.1:3000/user/photo to download the "stream" file

  1. The front end parses and downloads the "stream" file
<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>

Guess you like

Origin blog.csdn.net/Superman_H/article/details/130171512