通过 NestJS 实现与第三方接口的文件流无缝对接

引言

在当今的Web应用中,高效的文件上传至关重要。本文将介绍如何从前端页面调用NestJS提供的文件上传接口,并通过该接口将文件流直接上传至第三方服务,以此提升上传效率和用户体验。通过这一流程,我们将展示如何利用NestJS与外部存储系统无缝集成,实现流畅的文件处理机制。

具体操作流程

以下代码所有涉及的依赖默认您的项目都已经安装完毕。

1.前端页面
// upload.vue
<NUpload 
  ref="uploadRef"
  :custom-request="customRequest"
  :show-file-list="false"
>
  <NBoutton>文件上传</NButton>
</NUpload>

<script setup lang="ts">
    import { NButton, type UploadCustomRequestOptions } from 'naive-ui';
    import { uploadFile } from './UploadApi';
    import dayjs from 'dayjs';

     
    const uploadRef = ref(null);
    async function customRequest({ file }: UploadCustomRequestOptions) {
        // 修改文件名
        const modifiedFileName = `${dayjs().valueOf()}${file.name.substring(file.name.lastIndexOf('.'))}`;
        const modifiedFile = new File([file.file as File], modifiedFileName);
        const formData = new FormData();
        formData.append('file', modifiedFile);
        formData.append('prefix', 'avatar'); // 传多余参数

        // 调用上传接口
        const data = await uploadFile(formData);
        // ... 其他逻辑处理
    }
</script>

//  uploadApi.ts 

import { request } from '@/utils/http/axiosWrapper';

/**
 * 文件上传
 */
export async function uploadFile(
    formData: FormData
): Promise<any> {
    const response = await request.post('/bimface/upload', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
    });
    return response.data.data;
}
2.NestJs接口服务

让我们详细说明如何在 NestJS 服务器中将 file.buffer 写入文件中,然后使用 createReadStream 读取成文件流。

步骤概述

  • 接收上传的文件并保存到缓冲区 (file.buffer)

  • 将缓冲区数据写入临时文件。

  • 使用 createReadStream 读取文件流。

  • 发送文件流到远程服务器或其他处理逻辑。

示例代码

  • 创建文件上传控制器

src/app 目录下创建 upload.controller.ts

// src/app/upload.controller.ts
import {
    Controller, 
    Post, 
    UploadedFile,
    UseInterceptors, 
    Body,
} from '@nestjs/common';
import { UploadService } from './upload.service';
import { ApiBody, ApiConsumes, ApiOperation } from '@nestjs/swagger';
import { FileInterceptor } from '@nestjs/platform-express';
import {
    HubMeta,
    FolderInfo,
    ProjectInfo,
    TranslateFileStatus,
    TranslateFile,
} from './bfwrapper.dto';

@Controller('bimface')
export class UploadController {
    constructor(private readonly uploadService: UploadService) {}
    @Post('upload')
    @ApiOperation({ summary: '上传文件' })
    @UseInterceptors(FileInterceptor('file'))
    @ApiBody({
        schema: {
            type: 'object',
            properties: {
                file: {
                    type: 'string',
                    format: 'binary',
                },
            },
            required: ['file'],
        },
    })
    @ApiConsumes('multipart/form-data')
    uploadFile(@UploadedFile() file: Express.Multer.File) {
        return this.uploadService.upload(file);
    }
}
  • 创建流式文件处理服务

src/services 目录下创建 upload.service.ts

// src/services/upload.service.ts
import { createReadStream, createWriteStream, unlinkSync } from 'fs';
import { pipeline } from 'stream';
import { promisify } from 'util';
import axios from 'axios';
import { tmpdir } from 'os';

const pipelineAsync = promisify(pipeline);

export class UploadService {
  async upload(file: Express.Multer.File) {
     // 第三方普通文件流上传接口
     const requestUrl: string = https://api.bimface.com/bdfs/data/v1/projects/100000002/fileItems;
     
        // 处理文件名称中的中文乱码
        file.originalname = Buffer.from(file.originalname, 'latin1').toString('utf8');

        // 1.创建临时文件路径
        const tempFilePath = `${tmpdir()}/${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
        console.log('tempFilePath', tempFilePath);

        // 2.将缓冲区数据写入临时文件。
        const writeStream = createWriteStream(tempFilePath);
        await new Promise((resolve, reject) => {
            writeStream.on('finish', ()=> {
                resolve(tempFilePath);
            });
            writeStream.on('error', () => {
                reject(error);
            });
            writeStream.write(file.buffer);
            writeStream.end();
        });

        // 3.使用 createReadStream 读取文件流。
        const tempFileStream = createReadStream(tempFilePath);

        // 4.发送文件流到第三方服务器或其他处理逻辑。
        try {
            const { data } = await firstValueFrom(
                this.httpService.post(requestUrl, tempFileStream, {
                    params: {
                        name: file.originalname,
                        parentId: this.rootFolderId,
                        length: file.size,
                    },
                    headers: {
                        Authorization: this.token,
                        'Content-Length': file.size,
                        'Content-Type': 'application/octet-stream',
                    },
                }),
            );
            return data.data;
        } catch (error: any) {
            console.log(error);
        } finally {
            // 5.删除临时文件
            unlinkSync(tempFilePath);
        }
  }
}

解释

  • 接收上传的文件并保存到缓冲区 (file.buffer)

    uploadFile 方法中,通过 @UploadedFile() 接收上传的文件,并获取其 buffer 属性。

  • 将缓冲区数据写入临时文件

    handleBuffer 方法中,使用 createWriteStream 创建一个写入流,并将缓冲区数据写入临时文件。

  • 使用 createReadStream 读取文件流

    使用 createReadStream 创建一个读取流,从临时文件中读取数据。

  • 文件流到远程服务器或其他处理逻辑

    使用 Axios 发送文件流到远程服务器,并处理响应。

总结
通过上述步骤,你可以将上传的文件保存到缓冲区,然后写入临时文件,并使用 createReadStream 读取文件流进行进一步处理。这种方式可以有效地处理文件上传,并确保文件数据的完整性和安全性。

喜欢的话帮忙点个赞 + 关注吧,将持续更新 Nestjs 相关的文章,还可以关注我的公众号 梁三石FE ,感谢您的关注~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值