-1

I have written a code that should verify and convert every image that passes through it. I made use from nestjs, magic-bytes.js and Sharp. But i get an error that says:

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

Can someone help me fix this?

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;
  }
}

I have tried to remove the type by compressedFiles, but then i got another error that says:

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

protob
  • 3,317
  • 1
  • 8
  • 19
lucius0
  • 1
  • 3

2 Answers2

0

Buffer is not the same as an Express.Multer.File. Your compressedFiles constant is of type Buffer[] because of the files.map. What you are probably meaning to do is something like

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

Do note that you can't use async inside of an Array.prototype.map method and if you do need to use async you should wrap everything in Promise.all as I've done above

Jay McDoniel
  • 57,339
  • 7
  • 135
  • 147
0

I found that this works best for me. Thanks for the help, because i got a lot of inspiration from the answer that i got.

    import { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from "@nestjs/common";
    import { filetypemime } from "magic-bytes.js";
    import * as sharp from "sharp";
    import { Readable } from "stream";
    import { randomUUID } from "crypto";
    
    @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 mimeTypes = bytes.map((byte) => filetypemime(byte as any));
        if (mimeTypes.every((type) => type.includes("image"))) {
          throw new BadRequestException(
            `Validation failed (file should be an image), mimetype: ${mimeTypes}`,
          );
        }
    
        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 = await Promise.all(
          files.map(async (file) => {
            const buffer = await sharp(file.buffer).resize(320, 240).toFormat("webp").toBuffer();
            const newFile: Express.Multer.File = {
              fieldname: file.fieldname,
              originalname: `${randomUUID()}.webp`,
              encoding: file.encoding,
              mimetype: "image/webp",
              buffer: buffer,
              size: buffer.length,
              destination: "",
              stream: new Readable(),
              filename: "",
              path: "",
            };
            console.log(newFile);
            return newFile;
          }),
        );
        return Array.isArray(incomingFiles) ? compressedFiles : compressedFiles[0];
      }
    }
lucius0
  • 1
  • 3
  • Your answer only contains code. I recommend that you don't only post code as an answer, but _also_ provide an explanation about what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. Can you [edit] your answer to include this? – Jeremy Caney Apr 23 '23 at 19:05