最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - NestJS Multer Amazon S3 issues uploading multiple files - Stack Overflow

programmeradmin1浏览0评论

I'm using NestJS, Node, and Express for my backend and Angular for my frontend. I have a stepper where the user steps through and enters in information about themselves as well as a profile photo and any photos of their art that they want to post (it's a rough draft). I'm sending the files to the backend with this code:

<h2>Upload Some Photos</h2>
<label for="singleFile">Upload file</label>

<input id="singleFile" type="file" [fileUploadInputFor]=   "fileUploadQueue"/>
<br>

<mat-file-upload-queue #fileUploadQueue
    [fileAlias]="'file'"
    [httpUrl]="'http://localhost:3000/profile/artPhotos'">

    <mat-file-upload [file]="file" [id]="i" *ngFor="let file of fileUploadQueue.files; let i = index"></mat-file-upload>
</mat-file-upload-queue>

The front-end sends the photos as an array of files; I tried to change it so that it just sent a single file but could not get it working. I'm less focused on that because the user may need to upload multiple files, so I want to figure it out regardless. On the backend, I'm using multer, multer-s3, and AWS-SDK to help upload the files however it isn't working. Here is the controller code:

 @Post('/artPhotos')
    @UseInterceptors(FilesInterceptor('file'))
    async uploadArtPhotos(@Req() req, @Res() res): Promise<void> {
        req.file = req.files[0];
        delete req.files;
        // tslint:disable-next-line:no-console
        console.log(req);
        await this._profileService.fileupload(req, res);
    }

Here is ProfileService:

import { Profile } from './profile.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ProfileDto } from './dto/profile.dto';
import { Req, Res, Injectable, UploadedFile } from '@nestjs/mon';
import * as multer from 'multer';
import * as AWS from 'aws-sdk';
import * as multerS3 from 'multer-s3';

const AWS_S3_BUCKET_NAME = 'blah';
const s3 = new AWS.S3();
AWS.config.update({
  accessKeyId: 'blah',
  secretAccessKey: 'blah',
});


@Injectable()
export class ProfileService {

  constructor(@InjectRepository(Profile)
  private readonly profileRepository: Repository<Profile> ){}

  async createProfile( profileDto: ProfileDto ): Promise<void> {
    await this.profileRepository.save(profileDto);
  }

  async fileupload(@Req() req, @Res() res): Promise<void> {
    try {
      this.upload(req, res, error => {
        if (error) {
          // tslint:disable-next-line:no-console
          console.log(error);
          return res.status(404).json(`Failed to upload image file: ${error}`);
        }
        // tslint:disable-next-line:no-console
        console.log('error');
        return res.status(201).json(req.file);
      });
    } catch (error) {
      // tslint:disable-next-line:no-console
      console.log(error);
      return res.status(500).json(`Failed to upload image file: ${error}`);
    }
  }

  upload = multer({
    storage: multerS3({
      // tslint:disable-next-line:object-literal-shorthand
      s3: s3,
      bucket: AWS_S3_BUCKET_NAME,
      acl: 'public-read',
      // tslint:disable-next-line:object-literal-shorthand
      key: (req, file, cb) => {
        cb(null, `${Date.now().toString()} - ${file.originalname}`);
      },
    }),
  }).array('upload', 1);
}

I haven't implemented any middleware extending multer, but I don't think I have to. You can see in the controller I erase the files property on req and replace it with the file where it's value is just the first member of the files array but that was just to see if it would work if I send it something it was expecting, but it did not work then. Does anyone have any ideas regarding how I can fix this? Or can anyone at least point me in the right direction with a link to a relevant tutorial or something?

I'm using NestJS, Node, and Express for my backend and Angular for my frontend. I have a stepper where the user steps through and enters in information about themselves as well as a profile photo and any photos of their art that they want to post (it's a rough draft). I'm sending the files to the backend with this code:

<h2>Upload Some Photos</h2>
<label for="singleFile">Upload file</label>

<input id="singleFile" type="file" [fileUploadInputFor]=   "fileUploadQueue"/>
<br>

<mat-file-upload-queue #fileUploadQueue
    [fileAlias]="'file'"
    [httpUrl]="'http://localhost:3000/profile/artPhotos'">

    <mat-file-upload [file]="file" [id]="i" *ngFor="let file of fileUploadQueue.files; let i = index"></mat-file-upload>
</mat-file-upload-queue>

The front-end sends the photos as an array of files; I tried to change it so that it just sent a single file but could not get it working. I'm less focused on that because the user may need to upload multiple files, so I want to figure it out regardless. On the backend, I'm using multer, multer-s3, and AWS-SDK to help upload the files however it isn't working. Here is the controller code:

 @Post('/artPhotos')
    @UseInterceptors(FilesInterceptor('file'))
    async uploadArtPhotos(@Req() req, @Res() res): Promise<void> {
        req.file = req.files[0];
        delete req.files;
        // tslint:disable-next-line:no-console
        console.log(req);
        await this._profileService.fileupload(req, res);
    }

Here is ProfileService:

import { Profile } from './profile.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ProfileDto } from './dto/profile.dto';
import { Req, Res, Injectable, UploadedFile } from '@nestjs/mon';
import * as multer from 'multer';
import * as AWS from 'aws-sdk';
import * as multerS3 from 'multer-s3';

const AWS_S3_BUCKET_NAME = 'blah';
const s3 = new AWS.S3();
AWS.config.update({
  accessKeyId: 'blah',
  secretAccessKey: 'blah',
});


@Injectable()
export class ProfileService {

  constructor(@InjectRepository(Profile)
  private readonly profileRepository: Repository<Profile> ){}

  async createProfile( profileDto: ProfileDto ): Promise<void> {
    await this.profileRepository.save(profileDto);
  }

  async fileupload(@Req() req, @Res() res): Promise<void> {
    try {
      this.upload(req, res, error => {
        if (error) {
          // tslint:disable-next-line:no-console
          console.log(error);
          return res.status(404).json(`Failed to upload image file: ${error}`);
        }
        // tslint:disable-next-line:no-console
        console.log('error');
        return res.status(201).json(req.file);
      });
    } catch (error) {
      // tslint:disable-next-line:no-console
      console.log(error);
      return res.status(500).json(`Failed to upload image file: ${error}`);
    }
  }

  upload = multer({
    storage: multerS3({
      // tslint:disable-next-line:object-literal-shorthand
      s3: s3,
      bucket: AWS_S3_BUCKET_NAME,
      acl: 'public-read',
      // tslint:disable-next-line:object-literal-shorthand
      key: (req, file, cb) => {
        cb(null, `${Date.now().toString()} - ${file.originalname}`);
      },
    }),
  }).array('upload', 1);
}

I haven't implemented any middleware extending multer, but I don't think I have to. You can see in the controller I erase the files property on req and replace it with the file where it's value is just the first member of the files array but that was just to see if it would work if I send it something it was expecting, but it did not work then. Does anyone have any ideas regarding how I can fix this? Or can anyone at least point me in the right direction with a link to a relevant tutorial or something?

Share Improve this question edited Sep 9, 2018 at 16:14 Will asked Sep 9, 2018 at 1:27 WillWill 1531 gold badge3 silver badges13 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 8

My first guess would be that you are using the FileInterceptor and multer. I assume FileInterceptor adds multer in the controller which makes it available to the @UploadedFile decorator. Which could cause a conflict to your later use of multer. Try removing the interceptor and see if that fixes the issue.

Also I am attaching how I am doing file uploads. I am only uploading single images and I am using the AWS SDK so I don't have to work with multer directly, but here is how I am doing it, it might be helpful.

In the controller:

  @Post(':id/uploadImage')
  @UseInterceptors(FileInterceptor('file'))
  public uploadImage(@Param() params: any, @UploadedFile() file: any): Promise<Property> {
    return this.propertyService.addImage(params.id, file);
}

Then my service

/**
 * Returns a promise with the URL string.
 *
 * @param file
 */
public uploadImage(file: any, urlKey: string): Promise<string> {
  const params = {
    Body: file.buffer,
    Bucket: this.AWS_S3_BUCKET_NAME,
    Key: urlKey
  };

  return this.s3
    .putObject(params)
    .promise()
    .then(
      data => {
        return urlKey;
      },
      err => {
        return err;
      }
    );
}

Thanks Jedediah, I like how simple your code is. I copied your code however it still wasn't working. Turns out you have to instantiate the s3 object after you update the config with your accesskey and secretID.

发布评论

评论列表(0)

  1. 暂无评论