Loopback 4 File Upload

 

Trong bài viết này, chúng ta sẽ tìm hiểu cách xây dựng chức năng upload file trong Loopback 4 sử dụng multer — một middleware phổ biến cho việc xử lý multipart/form-data.

1. Chuẩn bị

Trước khi bắt đầu, bạn cần có một project Loopback 4 đã được khởi tạo. Nếu chưa có, hãy tham khảo bài viết Tìm hiểu & Cài đặt Loopback 4.

Cài đặt thư viện multer và type definition của nó:

npm install --save multer
npm install --save-dev @types/multer

2. Tạo FileUploadService

Tạo file src/services/file-upload.service.ts để cấu hình multer:

import {inject} from '@loopback/core';
import {Request} from '@loopback/rest';
import multer from 'multer';
import path from 'path';

const UPLOAD_DIR = path.join(__dirname, '../../uploads');

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, UPLOAD_DIR);
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
    cb(null, uniqueSuffix + path.extname(file.originalname));
  },
});

export const upload = multer({storage});

Lưu ý: Hãy tạo thư mục uploads ở thư mục gốc của project trước khi chạy.

mkdir uploads

3. Tạo FileUploadController

Tạo controller để xử lý request upload file:

lb4 controller

Chọn Empty Controller và đặt tên là FileUpload.

Sau đó chỉnh sửa file src/controllers/file-upload.controller.ts:

import {inject} from '@loopback/core';
import {
  post,
  Request,
  requestBody,
  Response,
  RestBindings,
} from '@loopback/rest';
import {upload} from '../services/file-upload.service';

export class FileUploadController {
  constructor(
    @inject(RestBindings.Http.REQUEST) private request: Request,
    @inject(RestBindings.Http.RESPONSE) private response: Response,
  ) {}

  @post('/upload', {
    responses: {
      '200': {
        description: 'Upload file thành công',
        content: {
          'application/json': {
            schema: {
              type: 'object',
              properties: {
                filename: {type: 'string'},
                originalname: {type: 'string'},
                size: {type: 'number'},
              },
            },
          },
        },
      },
    },
  })
  async uploadFile(
    @requestBody({
      description: 'multipart/form-data',
      required: true,
      content: {
        'multipart/form-data': {
          'x-parser': 'stream',
          schema: {
            type: 'object',
            properties: {
              file: {
                type: 'string',
                format: 'binary',
              },
            },
          },
        },
      },
    })
    request: Request,
  ): Promise<object> {
    return new Promise((resolve, reject) => {
      upload.single('file')(this.request, this.response, (err: unknown) => {
        if (err) {
          reject(err);
          return;
        }
        const file = (this.request as any).file;
        if (!file) {
          reject(new Error('Không tìm thấy file trong request'));
          return;
        }
        resolve({
          filename: file.filename,
          originalname: file.originalname,
          size: file.size,
          mimetype: file.mimetype,
        });
      });
    });
  }
}

4. Đăng ký Controller trong Application

Mở file src/application.ts và đảm bảo controller đã được import:

import {FileUploadController} from './controllers';

Nếu bạn dùng lb4 controller để tạo thì Loopback 4 sẽ tự động đăng ký controller vào src/controllers/index.ts.

5. Giới hạn loại file và kích thước

Để kiểm soát file upload chặt chẽ hơn, bạn có thể thêm fileFilterlimits vào cấu hình multer:

export const upload = multer({
  storage,
  limits: {
    fileSize: 5 * 1024 * 1024, // Tối đa 5MB
  },
  fileFilter: (req, file, cb) => {
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
    if (allowedTypes.includes(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('Loại file không được hỗ trợ'));
    }
  },
});

6. Upload nhiều file cùng lúc

Thay vì upload.single('file'), sử dụng upload.array('files', 10) để cho phép upload tối đa 10 file:

upload.array('files', 10)(this.request, this.response, (err: unknown) => {
  if (err) {
    reject(err);
    return;
  }
  const files = (this.request as any).files as Express.Multer.File[];
  resolve({
    count: files.length,
    files: files.map(f => ({
      filename: f.filename,
      originalname: f.originalname,
      size: f.size,
    })),
  });
});

Cập nhật schema trong @requestBody để nhận nhiều file:

schema: {
  type: 'object',
  properties: {
    files: {
      type: 'array',
      items: {
        type: 'string',
        format: 'binary',
      },
    },
  },
},

7. Kiểm tra với Loopback Explorer

Chạy project:

npm start

Truy cập http://127.0.0.1:3000/explorer, tìm endpoint POST /upload, chọn Try it out và chọn file để test.

Response trả về sẽ có dạng:

{
  "filename": "1734567890123-987654321.png",
  "originalname": "avatar.png",
  "size": 204800,
  "mimetype": "image/png"
}

8. Kết luận

Chúng ta đã hoàn thành việc xây dựng chức năng upload file trong Loopback 4 với multer:

  • Upload một file bằng upload.single()
  • Upload nhiều file bằng upload.array()
  • Giới hạn kích thướcloại file qua limitsfileFilter
  • Expose API qua Loopback Explorer để test trực tiếp

Bạn có thể mở rộng thêm bằng cách lưu thông tin file vào database thông qua Repository của Loopback 4 hoặc upload thẳng lên cloud storage như AWS S3, Google Cloud Storage.

Chúc bạn thành công!