You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用NestJS、Sharp处理图片时类型不匹配错误求助

NestJS图片处理Pipe类型错误修复

问题描述

使用NestJS、magic-bytes.js和Sharp编写图片验证转换Pipe时,出现以下类型错误:

Type 'Promise[]' is missing the following properties from type 'Promise<File | File[]>': then, catch, finally, [Symbol.toStringTag]ts(2739)

移除compressedFiles的类型声明后,新错误出现:

Type 'Promise[]' is not assignable to type 'File | File[]'

原代码如下:

import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from "@nestjs/common";
import { filetypemime } from "magic-bytes.js";
import * as sharp from "sharp";

@Injectable()
export class ParseFile implements PipeTransform {
  async transform(
    incomingFiles: Express.Multer.File | Express.Multer.File[],
    metadata: ArgumentMetadata,
  ): Promise<Express.Multer.File | Express.Multer.File[]> {
    const files = Array.isArray(incomingFiles) ? incomingFiles : [incomingFiles];

    const bytes = files.map((file) => new Uint8Array(file.buffer));

    const fileMimetype = filetypemime(bytes);
    if (fileMimetype) {
      console.log(`File type is ${fileMimetype}`);
    }

    if (incomingFiles === undefined || incomingFiles === null) {
      throw new BadRequestException("Validation failed (file expected)");
    }

    if (Array.isArray(incomingFiles) && incomingFiles.length === 0) {
      throw new BadRequestException("Validation failed (files expected)");
    }

    const compressedFiles: Promise<Express.Multer.File | Express.Multer.File[]> = files.map(
      async (file) => await sharp(file.buffer).resize(320, 240).toFormat("webp").toBuffer(),
    );

    return Array.isArray(incomingFiles) ? compressedFiles : compressedFiles;
  }
}

修复方案

问题根源

  1. files.map()返回Promise<Buffer>[],但错误地将其指定为Promise<Express.Multer.File | Express.Multer.File[]>类型,两者不兼容
  2. Sharp转换后仅返回Buffer,未包装成Pipe要求的Express.Multer.File对象
  3. 未使用Promise.all()等待所有异步转换任务完成,直接返回了Promise数组

修复后的代码

import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from "@nestjs/common";
import { filetypemime } from "magic-bytes.js";
import * as sharp from "sharp";

@Injectable()
export class ParseFile implements PipeTransform {
  async transform(
    incomingFiles: Express.Multer.File | Express.Multer.File[],
    metadata: ArgumentMetadata,
  ): Promise<Express.Multer.File | Express.Multer.File[]> {
    // 提前拦截无效请求
    if (!incomingFiles) {
      throw new BadRequestException("Validation failed (file expected)");
    }
    if (Array.isArray(incomingFiles) && incomingFiles.length === 0) {
      throw new BadRequestException("Validation failed (files expected)");
    }

    const files = Array.isArray(incomingFiles) ? incomingFiles : [incomingFiles];

    // 批量验证文件类型
    const fileMimetypes = files.map(file => filetypemime(new Uint8Array(file.buffer)));
    fileMimetypes.forEach((mimeType, index) => {
      if (mimeType) {
        console.log(`File ${index + 1} type is ${mimeType}`);
      }
    });

    // 等待所有转换完成,并包装为Multer.File对象
    const compressedFiles = await Promise.all(
      files.map(async (file) => {
        const convertedBuffer = await sharp(file.buffer)
          .resize(320, 240)
          .toFormat("webp")
          .toBuffer();
        
        // 更新文件属性,匹配Multer.File结构
        return {
          ...file,
          buffer: convertedBuffer,
          mimetype: "image/webp",
          filename: `${file.originalname.split('.')[0]}.webp`
        } as Express.Multer.File;
      })
    );

    // 保持返回格式与输入一致
    return Array.isArray(incomingFiles) ? compressedFiles : compressedFiles[0];
  }
}

关键修改点

  • 将参数校验移到顶部,提前拦截无效请求
  • 使用Promise.all()等待所有异步转换完成,确保返回处理后的文件而非Promise数组
  • 将Sharp返回的Buffer重新包装成Express.Multer.File对象,更新buffer、mimetype、filename等核心属性
  • 修复文件类型验证逻辑,支持批量文件的类型识别
  • 保证返回格式与输入一致:单个文件输入返回单个文件,数组输入返回数组

内容的提问来源于stack exchange,提问作者lucius0

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 07:22:41