-1

So I've been using LoopbackJS for the past 2-3 months and I'm currently building a file uploader (specifically for profile avatar image). But every time I sent an image file to the endpoint it never received by the server and tagged it as "undefined" which breaks the process. Can anyone help me identify what is wrong with my code?

Here are my codes currently.

file-upload.controller.ts

@post('/files', {
    responses: {
      '204': {
        description: 'Uploaded',
      },
    },
  })
  async fileUpload(
    @requestBody.file()
    request: Request,
    @inject(RestBindings.Http.RESPONSE) response: Response,
  ): Promise<string> {
    console.log(request.file.filename);

    const storage = multer.diskStorage({
      destination: './public/uploads',
      filename: function (req, file, cb) {
        cb(null, 'image-' + Date.now() + path.extname(file.filename));
      },
    });

    const upload = multer({storage: storage}).single('image');

    upload(request, response, (err: unknown) => {
      if (err) console.log(err);
      else {
        console.log(request.file.filename);
      }
    });

    return 'Yes';
  }

For anyone asking, I used the console.log(request.file.filename) to debug whether the request actually bring the file or not.

Here's my request using Postman

Error Log

Unhandled error in POST /files: 500 TypeError: Cannot read property 'filename' of undefined
    at FileUploadController.fileUpload (D:\Project\API\carena-api\src\controllers\file-upload.controller.ts:42:30)
    at invokeTargetMethod (D:\Project\API\carena-api\node_modules\@loopback\context\src\invocation.ts:255:47)
    at D:\Project\API\carena-api\node_modules\@loopback\context\src\invocation.ts:232:12
    at Object.transformValueOrPromise (D:\Project\API\carena-api\node_modules\@loopback\context\src\value-promise.ts:298:12)
    at invokeTargetMethodWithInjection (D:\Project\API\carena-api\node_modules\@loopback\context\src\invocation.ts:227:10)
    at InterceptedInvocationContext.invokeTargetMethod (D:\Project\API\carena-api\node_modules\@loopback\context\src\invocation.ts:118:14)       
    at targetMethodInvoker (D:\Project\API\carena-api\node_modules\@loopback\context\src\interceptor.ts:349:23)
    at D:\Project\API\carena-api\node_modules\@loopback\context\src\interceptor-chain.ts:218:14
    at Object.transformValueOrPromise (D:\Project\API\carena-api\node_modules\@loopback\context\src\value-promise.ts:298:12)
    at GenericInterceptorChain.invokeNextInterceptor (D:\Project\API\carena-api\node_modules\@loopback\context\src\interceptor-chain.ts:213:12)  
    at GenericInterceptorChain.next (D:\Project\API\carena-api\node_modules\@loopback\context\src\interceptor-chain.ts:201:17)
    at GenericInterceptorChain.invokeInterceptors (D:\Project\API\carena-api\node_modules\@loopback\context\src\interceptor-chain.ts:178:17)     
    at Object.invokeInterceptors (D:\Project\API\carena-api\node_modules\@loopback\context\src\interceptor-chain.ts:250:16)
    at D:\Project\API\carena-api\node_modules\@loopback\context\src\interceptor.ts:351:14
    at tryCatchFinally (D:\Project\API\carena-api\node_modules\@loopback\context\src\value-promise.ts:222:14)
    at Object.tryWithFinally (D:\Project\API\carena-api\node_modules\@loopback\context\src\value-promise.ts:197:10)

Thank you.

MEGAtive
  • 3
  • 3
  • try `@requestBody({description: 'Upload file test', required: true, content: { 'multipart/form-data': {'x-parser': 'stream', schema: {type: 'object'}, }, },}) request: Request,` – Salitha Jul 01 '20 at 07:39
  • @Salitha It's still not working. The error is still the same one as before. – MEGAtive Jul 01 '20 at 07:49
  • Files are usually received as an array in `request.files`. So try `console.log(request.files[0].originalname)` – Salitha Jul 01 '20 at 09:34
  • @Salitha it still says 'undefined'. – MEGAtive Jul 01 '20 at 10:41

1 Answers1

0

The console log at the top of the code cause the error. You should verify the error with line number. The debug mode in any IDE is helpful to figure out what went wrong. Here is the corrected code

@post('/files')
  async fileUpload(
    @requestBody({
      content: {
        'multipart/form-data': {
          'x-parser': 'stream',
          schema: {type: 'object'},
        },
      },
    })
    request: Request,
    @inject(RestBindings.Http.RESPONSE) response: Response,
  ): Promise<string> {
    const storage = multer.memoryStorage();
    const upload = multer({storage});
    const fileArr = await new Promise<any[]>((resolve, reject) => {
      upload.any()(<any>request, <any>response, err => {
        if (err) reject(err);
        else {
          resolve(<any[]>request.files);
        }
      });
    });
    console.log(fileArr[0].originalname);

    ....
    ....
    ....
  
    return 'Yes';
  }

Use fileArr[0].buffer to write file to the disk

Salitha
  • 1,022
  • 1
  • 12
  • 32
  • Okay, I found what is the problem with my code. Above code works, but I need to use diskStorage to store my files locally. So I change it to fit diskStorage. After that I found out that the requests are indeed received, but variable ```filename``` in particular is undefined. Further research I found that ```filename``` actually stores the new filename, not the original. So I changed that bit into ```file.originalname``` and now it works. – MEGAtive Jul 01 '20 at 12:41