Awesome
<h1 align="center"> <div align="center"> <a href="http://nestjs.com/" target="_blank"> <img src="./media/header-light-theme.png" width="600" alt="NestJS Multer Extended Logo" /> </a> </div> </h1> <div align="center"> <a href="https://www.npmjs.com/package/nestjs-multer-extended"> <img src="https://img.shields.io/npm/v/nestjs-multer-extended.svg" alt="NPM Version" /> </a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/blob/master/LICENSE"> <img src="https://img.shields.io/github/license/jeffminsungkim/nestjs-multer-extended.svg" alt="license" /> </a> <a href="https://circleci.com/gh/jeffminsungkim/nestjs-multer-extended" target="_blank"> <img src="https://img.shields.io/circleci/build/github/jeffminsungkim/nestjs-multer-extended/master" alt="CircleCI" /> </a> <a href='https://coveralls.io/github/jeffminsungkim/nestjs-multer-extended?branch=master'> <img src='https://coveralls.io/repos/github/jeffminsungkim/nestjs-multer-extended/badge.svg?branch=master&service=github' alt='Coverage Status' /> </a> <a href="https://nestjs.com" target="_blank"> <img src="https://img.shields.io/badge/built%20with-NestJs-e0234e.svg" alt="Built with NestJS" /> </a> <a href="https://github.com/nestjsplus/dyn-schematics" target="_blank"> <img src="https://img.shields.io/badge/Built%20with-%40nestjsplus%2Fdyn--schematics-brightgreen" alt="Built with @nestjsplus/dyn-schematics" /> </a> <a href="http://commitizen.github.io/cz-cli/" target="_blank"> <img src="https://img.shields.io/badge/commitizen-friendly-brightgreen.svg" alt="Commitizen"/> </a> <a href="https://github.com/juliandavidmr/awesome-nestjs#components--libraries"> <img src="https://awesome.re/mentioned-badge.svg" alt="Awesome Nest" /> </a> <img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg" alt="PRs welcome" /> </div>Features
- Single file upload to an Amazon S3 bucket
- Support for dynamic paths, upload files wherever you want!
- Generate thumbnail image along with the original
- Resize single image or even make it into different sizes
- Load AWS S3 configuration at runtime
Installation
NPM
$ npm i -s nestjs-multer-extended
Yarn
$ yarn add nestjs-multer-extended
Getting started
Once the installation process is complete, we can import the module either synchronously or asynchronosly into the root AppModule
.
Β
Synchronous configuration
import { Module } from '@nestjs/common';
import { MulterExtendedModule } from 'nestjs-multer-extended';
@Module({
imports: [
MulterExtendedModule.register({
awsConfig: {
accessKeyId: 'YOUR_AWS_ACCESS_KEY_ID',
secretAccessKey: 'YOUR_AWS_ACCESS_KEY_ID',
region: 'AWS_REGION_NEAR_TO_YOU',
// ... any options you want to pass to the AWS instance
},
bucket: 'YOUR_S3_BUCKET_NAME',
basePath: 'ROOT_DIR_OF_ASSETS',
fileSize: 1 * 1024 * 1024,
}),
],
})
export class AppModule {}
Asynchronous configuration
In this example, the module integrates with the awesome nestjs-config package.
useFactory
should return an object with MulterExtendedS3Options interface or undefined.
import { Module } from '@nestjs/common';
import { MulterExtendedModule } from 'nestjs-multer-extended';
import { ConfigService } from 'nestjs-config';
@Module({
imports: [
MulterExtendedModule.registerAsync({
useFactory: (config: ConfigService) => config.get('s3'),
inject: [ConfigService],
}),
],
})
export class AppModule {}
Note: You can import this module from not only the root module of your app but also from other feature modules where you want to use it.
Β
To upload a single file, simply tie the AmazonS3FileInterceptor()
interceptor to the route handler and extract file
from the request using the @UploadedFile()
decorator.
import { Controller, Post, UseInterceptors, UploadedFile } from '@nestjs/common';
import { AmazonS3FileInterceptor } from 'nestjs-multer-extended';
@Controller()
export class AppController {
@Post('upload')
@UseInterceptors(AmazonS3FileInterceptor('file'))
uploadFile(@UploadedFile() file) {
console.log(file);
}
}
In this example, uploadFile()
method will upload a file under the base path you have configured earlrier.
The AmazonS3FileInterceptor()
decorator takes two arguments:
fieldName
: string that supplies the name of the field from the HTML form that holds a file.options
: optional object of typeMulterExtendedOptions
. (mode details here)
What if you wanted to upload a file in a different location under the base path? Thankfully, AmazonS3FileInterceptor()
decorator accepts dynamicPath
property as a second argument option. Pass the string path as shown below:
@Post('upload')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
dynamicPath: 'aec16138-a75a-4961-b8c1-8e803b6bf2cf'
}),
)
uploadFile(@UploadedFile() file) {
console.log(file);
}
In this example, uploadFile()
method will upload a file in ${basePath}/aec16138-a75a-4961-b8c1-8e803b6bf2cf/${originalname}
.
Route parameters can also be used as a key. For example, if you have the route /user/:name
, then pass the name
into the dynamicPath
property as a value.
@Post('user/:name')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
dynamicPath: 'name'
}),
)
uploadFile(@UploadedFile() file) {
// POST /user/jeffminsungkim
console.log(file);
// => YOUR-BASE-PATH/jeffminsungkim/filename.png
}
@Post('user/:name/team/:no')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
dynamicPath: 'no'
}),
)
uploadFile(@UploadedFile() file) {
// POST /user/jeffminsungkim/team/8987
console.log(file);
// => YOUR-BASE-PATH/8987/filename.png
}
@Post('user/:name/team/:no')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
dynamicPath: ['name', 'no']
}),
)
uploadFile(@UploadedFile() file) {
// POST /user/jeffminsungkim/team/8987
console.log(file);
// => YOUR-BASE-PATH/jeffminsungkim/8987/filename.png
}
Β
You may want to store the file with an arbitrary name instead of the original file name. You can do this by passing the randomFilename
property attribute set to true
as follows:
@Post('upload')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
randomFilename: true
}),
)
uploadFile(@UploadedFile() file) {
console.log(file);
}
If you want to resize the file before the upload, you can pass on the resize
property as follows:
@Post('upload')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
resize: { width: 500, height: 400 },
}),
)
uploadFile(@UploadedFile() file) {
console.log(file);
}
You can pass an array of size options to resize a single image into different sizes as follows:
@Post('upload')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
resizeMultiple: [
{ suffix: 'sm', width: 200, height: 200 },
{ suffix: 'md', width: 300, height: 300 },
{ suffix: 'lg', width: 400, height: 400 },
],
}
)
uploadFile(@UploadedFile() file) {
console.log(file);
}
Not only creating a thumbnail image but also willing to change the file size limit, you can pass the properties as follows:
@Post('upload')
@UseInterceptors(
AmazonS3FileInterceptor('file', {
thumbnail: { suffix: 'thumb', width: 200, height: 200 },
limits: { fileSize: 7 * 1024 * 1024 },
}),
)
uploadFile(@UploadedFile() file) {
console.log(file);
}
In this example, uploadFile()
method will upload both thumbnail and original images.
Β
MulterExtendedS3Options
MulterExtendedModule
requires an object with the following interface:
interface MulterExtendedS3Options {
/**
* AWS Access Key ID
* @deprecated v2 use awsConfig instead
*/
readonly accessKeyId?: string;
/**
* AWS Secret Access Key
* @deprecated v2 use awsConfig instead
*/
readonly secretAccessKey?: string;
/**
* Default region name
* default: us-west-2
* @deprecated v2 use awsConfig instead
*/
readonly region?: string;
/**
* AWS Config
*/
readonly awsConfig?: ConfigurationOptions & ConfigurationServicePlaceholders & APIVersions & {[key: string]: any};
/**
* S3 Config
*/
readonly s3Config?: AWS.S3.Types.ClientConfiguration;
/**
* The name of Amazon S3 bucket
*/
readonly bucket: string;
/**
* The base path where you want to store files in
*/
readonly basePath: string;
/**
* Optional parameter for Access control level for the file
* default: public-read
* @see https://docs.aws.amazon.com/AmazonS3/latest/dev/acl-overview.html#canned-acl
*/
readonly acl?: string;
/**
* AWS Endpoint
* @deprecated v2 use s3Config instead
*/
readonly endpoint?: string;
/**
* Optional parameter for the file size
* default: 3MB
*/
readonly fileSize?: number | string;
/**
* Optional parameter for a custom logger
* default: NestJS built-in text-based logger
* @see https://docs.nestjs.com/techniques/logger
*/
readonly logger?: LoggerService;
}
MulterExtendedOptions
Key | Default | Description | Example |
---|---|---|---|
dynamicPath | undefined | The name that you assign to an S3 object | "aec16138-a75a-4961-b8c1-8e803b6bf2cf/random/dir" |
randomFilename | undefined | If this property sets to true, a random file name will be generated | "aec16138-a75a-4961-b8c1-8e803b6bf2cf" |
fileFilter | Accepts JPEG, PNG types only | Function to control which files are accepted | |
limits | 3MB | Limits of the uploaded data | 5242880 (in bytes) |
resize | undefined | Resize a single file | { width: 300, height: 350 } |
resizeMultiple | undefined | Resize a single file into different sizes (Array<object> ) | [{ suffix: 'md', width: 300, height: 350 }, { suffix: 'sm', width: 200, height: 200 }] |
thumbnail | undefined | Create a thumbnail image (object ) | { suffix: 'thumbnail', width: 200, height: 200 } |
Support
<a href="https://www.buymeacoffee.com/jeffminsungkim" target="_blank"> <img src="https://badgen.net/badge/Buy%20Me/A%20Coffee/orange?icon=kofi" alt="Buymeacoffee" /> </a>You could help me out for some coffees π₯€ or give us a star βοΈ
Maintainers
Contributors β¨
Thanks goes to these wonderful people (emoji key):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> <!-- prettier-ignore-start --> <!-- markdownlint-disable --> <table> <tr> <td align="center"><a href="https://jeffminsungkim.com"><img src="https://avatars1.githubusercontent.com/u/6092023?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Minsung Kim</b></sub></a><br /><a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=jeffminsungkim" title="Code">π»</a> <a href="#maintenance-jeffminsungkim" title="Maintenance">π§</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=jeffminsungkim" title="Documentation">π</a> <a href="#infra-jeffminsungkim" title="Infrastructure (Hosting, Build-Tools, etc)">π</a> <a href="#ideas-jeffminsungkim" title="Ideas, Planning, & Feedback">π€</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=jeffminsungkim" title="Tests">β οΈ</a></td> <td align="center"><a href="https://github.com/jmcdo29"><img src="https://avatars3.githubusercontent.com/u/28268680?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jay McDoniel</b></sub></a><br /><a href="#ideas-jmcdo29" title="Ideas, Planning, & Feedback">π€</a> <a href="#tool-jmcdo29" title="Tools">π§</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/pulls?q=is%3Apr+reviewed-by%3Ajmcdo29" title="Reviewed Pull Requests">π</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=jmcdo29" title="Code">π»</a></td> <td align="center"><a href="https://github.com/semin3276"><img src="https://avatars1.githubusercontent.com/u/60590414?v=4?s=100" width="100px;" alt=""/><br /><sub><b>semin3276</b></sub></a><br /><a href="#design-semin3276" title="Design">π¨</a></td> <td align="center"><a href="https://www.rene-volbach.de"><img src="https://avatars0.githubusercontent.com/u/18092644?v=4?s=100" width="100px;" alt=""/><br /><sub><b>RenΓ© Volbach</b></sub></a><br /><a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=volbrene" title="Code">π»</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=volbrene" title="Tests">β οΈ</a></td> <td align="center"><a href="http://grainer.io"><img src="https://avatars0.githubusercontent.com/u/14952013?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gimyboya</b></sub></a><br /><a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=gimyboya" title="Code">π»</a></td> <td align="center"><a href="https://github.com/dineshsalunke"><img src="https://avatars2.githubusercontent.com/u/52815633?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dineshsalunke</b></sub></a><br /><a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=dineshsalunke" title="Code">π»</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=dineshsalunke" title="Documentation">π</a></td> <td align="center"><a href="http://michaelwolz.de"><img src="https://avatars3.githubusercontent.com/u/7479806?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Wolz</b></sub></a><br /><a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=michaelwolz" title="Code">π»</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=michaelwolz" title="Documentation">π</a></td> </tr> <tr> <td align="center"><a href="https://visurel.com"><img src="https://avatars3.githubusercontent.com/u/7209649?v=4?s=100" width="100px;" alt=""/><br /><sub><b>visurel</b></sub></a><br /><a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=visurel" title="Code">π»</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=visurel" title="Documentation">π</a></td> <td align="center"><a href="http://tevpro.com"><img src="https://avatars.githubusercontent.com/u/1214744?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Keith Kikta</b></sub></a><br /><a href="#maintenance-newbish" title="Maintenance">π§</a> <a href="https://github.com/jeffminsungkim/nestjs-multer-extended/commits?author=newbish" title="Code">π»</a></td> </tr> </table> <!-- markdownlint-restore --> <!-- prettier-ignore-end --> <!-- ALL-CONTRIBUTORS-LIST:END -->This project follows the all-contributors specification. Contributions of any kind welcome!