nestjs-form-data
Advanced tools
Comparing version 1.7.1 to 1.8.0
@@ -10,2 +10,3 @@ "use strict"; | ||
const common_1 = require("@nestjs/common"); | ||
const file_type_1 = __importDefault(require("file-type")); | ||
class FormReader { | ||
@@ -87,3 +88,10 @@ constructor(req, config) { | ||
async loadFile(originalName, encoding, mimetype, stream) { | ||
return await this.config['storage'].create(originalName, encoding, mimetype, stream, this.config); | ||
const streamWithFileType = await file_type_1.default.stream(stream); | ||
const storedFile = await this.config['storage'].create({ | ||
originalName, | ||
encoding, | ||
mimetype, | ||
}, streamWithFileType, this.config); | ||
storedFile.setFileTypeResult(streamWithFileType.fileType); | ||
return storedFile; | ||
} | ||
@@ -90,0 +98,0 @@ } |
/// <reference types="node" /> | ||
import { StoredFile } from './StoredFile'; | ||
import { FormDataInterceptorConfig } from '../../interfaces/FormDataInterceptorConfig'; | ||
import { Readable as ReadableStream } from 'node:stream'; | ||
import { ParticleStoredFile } from '../../interfaces/ParticleStoredFile'; | ||
export declare class FileSystemStoredFile extends StoredFile { | ||
mimetype: string; | ||
encoding: string; | ||
originalName: string; | ||
path: string; | ||
size: number; | ||
path: string; | ||
static create(originalName: any, encoding: any, mimetype: any, stream: NodeJS.ReadableStream, config: FormDataInterceptorConfig): Promise<FileSystemStoredFile>; | ||
static create(busboyFileMeta: ParticleStoredFile, stream: ReadableStream, config: FormDataInterceptorConfig): Promise<FileSystemStoredFile>; | ||
private static makeFileNameWithSalt; | ||
@@ -12,0 +11,0 @@ delete(): Promise<void>; |
@@ -26,3 +26,2 @@ "use strict"; | ||
exports.FileSystemStoredFile = void 0; | ||
const class_transformer_1 = require("class-transformer"); | ||
const StoredFile_1 = require("./StoredFile"); | ||
@@ -33,6 +32,7 @@ const mkdirp_1 = __importDefault(require("mkdirp")); | ||
const uid_1 = require("uid"); | ||
const class_transformer_1 = require("class-transformer"); | ||
class FileSystemStoredFile extends StoredFile_1.StoredFile { | ||
static async create(originalName, encoding, mimetype, stream, config) { | ||
static async create(busboyFileMeta, stream, config) { | ||
await mkdirp_1.default.native(config.fileSystemStoragePath); | ||
const filePath = path_1.default.resolve(config.fileSystemStoragePath, FileSystemStoredFile.makeFileNameWithSalt(originalName)); | ||
const filePath = path_1.default.resolve(config.fileSystemStoragePath, FileSystemStoredFile.makeFileNameWithSalt(busboyFileMeta.originalName)); | ||
return new Promise((res, rej) => { | ||
@@ -45,5 +45,5 @@ const outStream = fs.createWriteStream(filePath); | ||
const file = class_transformer_1.plainToClass(FileSystemStoredFile, { | ||
originalName, | ||
encoding, | ||
mimetype, | ||
originalName: busboyFileMeta.originalName, | ||
encoding: busboyFileMeta.encoding, | ||
busBoyMimeType: busboyFileMeta.mimetype, | ||
path: filePath, | ||
@@ -50,0 +50,0 @@ size, |
/// <reference types="node" /> | ||
import { StoredFile } from './StoredFile'; | ||
import { FormDataInterceptorConfig } from '../../interfaces/FormDataInterceptorConfig'; | ||
import { Readable as ReadableStream } from 'node:stream'; | ||
import { ParticleStoredFile } from '../../interfaces/ParticleStoredFile'; | ||
export declare class MemoryStoredFile extends StoredFile { | ||
mimetype: string; | ||
encoding: string; | ||
originalName: string; | ||
size: number; | ||
buffer: Buffer; | ||
static create(originalName: any, encoding: any, mimetype: any, stream: NodeJS.ReadableStream, config: FormDataInterceptorConfig): Promise<MemoryStoredFile>; | ||
static create(busboyFileMeta: ParticleStoredFile, stream: ReadableStream, config: FormDataInterceptorConfig): Promise<MemoryStoredFile>; | ||
delete(): Promise<void>; | ||
} | ||
//# sourceMappingURL=MemoryStoredFile.d.ts.map |
"use strict"; | ||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { | ||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; | ||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); | ||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; | ||
return c > 3 && r && Object.defineProperty(target, key, r), r; | ||
}; | ||
var __metadata = (this && this.__metadata) || function (k, v) { | ||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -16,13 +7,13 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
exports.MemoryStoredFile = void 0; | ||
const class_transformer_1 = require("class-transformer"); | ||
const StoredFile_1 = require("./StoredFile"); | ||
const concat_stream_1 = __importDefault(require("concat-stream")); | ||
const class_transformer_1 = require("class-transformer"); | ||
class MemoryStoredFile extends StoredFile_1.StoredFile { | ||
static create(originalName, encoding, mimetype, stream, config) { | ||
static create(busboyFileMeta, stream, config) { | ||
return new Promise((res, rej) => { | ||
stream.pipe(concat_stream_1.default({ encoding: 'buffer' }, (buffer) => { | ||
const file = class_transformer_1.plainToClass(MemoryStoredFile, { | ||
originalName, | ||
encoding, | ||
mimetype, | ||
originalName: busboyFileMeta.originalName, | ||
encoding: busboyFileMeta.encoding, | ||
busBoyMimeType: busboyFileMeta.mimetype, | ||
buffer, | ||
@@ -39,7 +30,3 @@ size: buffer.length, | ||
} | ||
__decorate([ | ||
class_transformer_1.Transform((params) => (params.value instanceof Buffer) ? params.value : null), | ||
__metadata("design:type", Buffer) | ||
], MemoryStoredFile.prototype, "buffer", void 0); | ||
exports.MemoryStoredFile = MemoryStoredFile; | ||
//# sourceMappingURL=MemoryStoredFile.js.map |
/// <reference types="node" /> | ||
import { ParticleStoredFile } from '../../interfaces/ParticleStoredFile'; | ||
import { FormDataInterceptorConfig } from '../../interfaces/FormDataInterceptorConfig'; | ||
import { Readable as ReadableStream } from 'node:stream'; | ||
import { FileTypeResult } from 'file-type/core'; | ||
import { MetaFieldSource } from '../../interfaces/MetaFieldSource'; | ||
export declare abstract class StoredFile implements ParticleStoredFile { | ||
abstract mimetype: string; | ||
abstract encoding: string; | ||
abstract originalName: string; | ||
encoding: string; | ||
originalName: string; | ||
abstract size: number; | ||
static create(originalName: any, encoding: any, mimetype: any, stream: NodeJS.ReadableStream, config: FormDataInterceptorConfig): Promise<StoredFile>; | ||
protected busBoyMimeType: string; | ||
protected fileType: FileTypeResult; | ||
static create(busboyFileMeta: ParticleStoredFile, stream: ReadableStream, config: FormDataInterceptorConfig): Promise<StoredFile>; | ||
setFileTypeResult(fileType: FileTypeResult): void; | ||
get mimeType(): string; | ||
get mimetype(): string; | ||
get extension(): string; | ||
get mimeTypeWithSource(): MetaFieldSource; | ||
get extensionWithSource(): MetaFieldSource; | ||
abstract delete(): Promise<void>; | ||
} | ||
//# sourceMappingURL=StoredFile.d.ts.map |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.StoredFile = void 0; | ||
const MetaFieldSource_1 = require("../../interfaces/MetaFieldSource"); | ||
const path_1 = __importDefault(require("path")); | ||
class StoredFile { | ||
static create(originalName, encoding, mimetype, stream, config) { | ||
static create(busboyFileMeta, stream, config) { | ||
throw new Error(`Static method create must be implemented`); | ||
} | ||
; | ||
setFileTypeResult(fileType) { | ||
this.fileType = fileType; | ||
} | ||
get mimeType() { | ||
var _a; | ||
return ((_a = this.fileType) === null || _a === void 0 ? void 0 : _a.mime) || this.busBoyMimeType; | ||
} | ||
// Compatibility with previous versions | ||
get mimetype() { | ||
return this.mimeType; | ||
} | ||
get extension() { | ||
var _a; | ||
return ((_a = this.fileType) === null || _a === void 0 ? void 0 : _a.ext) || (path_1.default.parse(this.originalName).ext || '').replace('.', ''); | ||
} | ||
get mimeTypeWithSource() { | ||
var _a; | ||
const value = this.mimeType; | ||
const source = (!!((_a = this.fileType) === null || _a === void 0 ? void 0 : _a.mime)) ? MetaFieldSource_1.MetaSource.bufferMagicNumber : MetaFieldSource_1.MetaSource.contentType; | ||
return { value, source }; | ||
} | ||
get extensionWithSource() { | ||
var _a; | ||
const value = this.extension; | ||
const source = (!!((_a = this.fileType) === null || _a === void 0 ? void 0 : _a.ext)) ? MetaFieldSource_1.MetaSource.bufferMagicNumber : MetaFieldSource_1.MetaSource.contentType; | ||
return { value, source }; | ||
} | ||
} | ||
exports.StoredFile = StoredFile; | ||
//# sourceMappingURL=StoredFile.js.map |
export * from './form-data'; | ||
export * from './validation/has-mime-type.validator'; | ||
export * from './validation/is-file.validator'; | ||
@@ -7,2 +6,4 @@ export * from './validation/is-files.validator'; | ||
export * from './validation/min-file-size.validator'; | ||
export * from './validation/has-mime-type.validator'; | ||
export * from './validation/has-extension.validator'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -14,3 +14,2 @@ "use strict"; | ||
__exportStar(require("./form-data"), exports); | ||
__exportStar(require("./validation/has-mime-type.validator"), exports); | ||
__exportStar(require("./validation/is-file.validator"), exports); | ||
@@ -20,2 +19,4 @@ __exportStar(require("./validation/is-files.validator"), exports); | ||
__exportStar(require("./validation/min-file-size.validator"), exports); | ||
__exportStar(require("./validation/has-mime-type.validator"), exports); | ||
__exportStar(require("./validation/has-extension.validator"), exports); | ||
//# sourceMappingURL=index.js.map |
import { ValidationOptions } from 'class-validator'; | ||
export declare function HasMimeType(allowedMimeTypes: string[], validationOptions?: ValidationOptions): PropertyDecorator; | ||
import { MetaSource } from '../../interfaces/MetaFieldSource'; | ||
export declare function HasMimeType(allowedMimeTypes: string[] | string, strictSource?: MetaSource | ValidationOptions, validationOptions?: ValidationOptions): PropertyDecorator; | ||
//# sourceMappingURL=has-mime-type.validator.d.ts.map |
@@ -6,3 +6,4 @@ "use strict"; | ||
const is_file_validator_1 = require("./is-file.validator"); | ||
function HasMimeType(allowedMimeTypes, validationOptions) { | ||
const toArray_1 = require("../../helpers/toArray"); | ||
function HasMimeType(allowedMimeTypes, strictSource, validationOptions) { | ||
return class_validator_1.ValidateBy({ | ||
@@ -13,5 +14,9 @@ name: 'HasMimeType', | ||
validate(value, args) { | ||
const allowedMimeTypes = args.constraints[0] || []; | ||
const allowedMimeTypes = toArray_1.toArray(args.constraints[0]) || []; | ||
const strictSource = (typeof args.constraints[1] === 'string') | ||
? args.constraints[1] | ||
: undefined; | ||
if (is_file_validator_1.isFile(value)) { | ||
return allowedMimeTypes.includes(value.mimetype); | ||
const mimeWithSource = value.mimeTypeWithSource; | ||
return allowedMimeTypes.includes(mimeWithSource.value) && (!strictSource || strictSource === mimeWithSource.source); | ||
} | ||
@@ -21,9 +26,9 @@ return false; | ||
defaultMessage(validationArguments) { | ||
const allowedMimeTypes = validationArguments.constraints[0] || []; | ||
const allowedMimeTypes = toArray_1.toArray(validationArguments.constraints[0]) || []; | ||
return `File must be of one of the types ${allowedMimeTypes.join(', ')}`; | ||
}, | ||
}, | ||
}, validationOptions); | ||
}, validationOptions || ((typeof strictSource === 'object') ? strictSource : null)); | ||
} | ||
exports.HasMimeType = HasMimeType; | ||
//# sourceMappingURL=has-mime-type.validator.js.map |
{ | ||
"name": "nestjs-form-data", | ||
"version": "1.7.1", | ||
"version": "1.8.0", | ||
"description": "NestJS middleware for handling multipart/form-data, which is primarily used for uploading files", | ||
@@ -14,3 +14,3 @@ "main": "dist/index", | ||
"prepublish": "npm run test:e2e && npm run build", | ||
"test:e2e": "jest --config ./test/jest-e2e.json" | ||
"test:e2e": "jest --config test/jest-e2e.js" | ||
}, | ||
@@ -56,2 +56,3 @@ "author": "dmitriy-nz", | ||
"concat-stream": "^2.0.0", | ||
"file-type": "^16.5.4", | ||
"mkdirp": "^1.0.4", | ||
@@ -58,0 +59,0 @@ "type-is": "^1.6.18", |
@@ -19,2 +19,4 @@ [![npm version](https://badge.fury.io/js/nestjs-form-data.svg)](https://badge.fury.io/js/nestjs-form-data) | ||
[Changelog](blob/master/CHANGELOG.md) | ||
## Installation | ||
@@ -153,13 +155,59 @@ ```sh | ||
By default, several validators are available with which you can check the file | ||
Note: If you need to validate an array of files for size or otherwise, use `each: true` property from `ValidationOptions` | ||
`@IsFile()` - Checks if the value is an uploaded file | ||
`@IsFiles()` - Checks an array of files | ||
`@MaxFileSize()` - Maximum allowed file size | ||
`@MinFileSize()` - Minimum allowed file size | ||
`@HasMimeType()` - Check the mime type of the file | ||
### IsFile | ||
Checks if the value is an uploaded file | ||
```ts | ||
@IsFile(validationOptions?: ValidationOptions) | ||
``` | ||
If you need to validate an array of files for size or otherwise, use `each: true` property from `ValidationOptions` | ||
### IsFiles | ||
Checks an array of files, the same as `@IsFile({ each: true })` | ||
For convenience | ||
```ts | ||
@IsFiles(validationOptions?: ValidationOptions) | ||
``` | ||
### MaxFileSize | ||
Maximum allowed file size | ||
```ts | ||
@MaxFileSize(maxSizeBytes: number, validationOptions?: ValidationOptions) | ||
``` | ||
### MinFileSize | ||
Minimum allowed file size | ||
```ts | ||
@MinFileSize(minSizeBytes: number, validationOptions?: ValidationOptions) | ||
``` | ||
### HasMimeType | ||
Check the mime type of the file | ||
The library uses two sources to get the mime type for the file: | ||
- [file-type](https://www.npmjs.com/package/file-type) library gets mime-type: gets the mime-type from the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) directly from the binary data, it is a reliable source because it checks the file itself but may not return values for some files | ||
- content type header from [busboy](https://www.npmjs.com/package/busboy: is a less trusted source because it can be tampered with | ||
*Priority of receiving mime-type corresponds to the list* | ||
The default is simple mode, which does not check the data source, but you can pass a second argument to strictly check the mime-type and data source. | ||
You can also get the mime type and data source via the `get mimeTypeWithSource():MetaFieldSource` getter on the `StoredFile` | ||
```ts | ||
@HasMimeType(allowedMimeTypes: string[] | string, strictSource?: MetaSource | ValidationOptions, validationOptions?: ValidationOptions) | ||
``` | ||
### HasExtension | ||
Check the extension type of the file | ||
The library uses two sources to get the extension for the file: | ||
- [file-type](https://www.npmjs.com/package/file-type) library gets mime-type: gets the extension from the [magic number](https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files) directly from the binary data, it is a reliable source because it checks the file itself but may not return values for some files | ||
- value after the last dot in file name: is a less trusted source because it can be tampered with | ||
*Priority of receiving extension corresponds to the list* | ||
The default is simple mode, which does not check the data source, but you can pass a second argument to strictly check the extension and data source. | ||
You can also get the extension and data source via the `get extensionWithSource():MetaFieldSource` getter on the `StoredFile` | ||
```ts | ||
@HasExtension(allowedMimeTypes: string[] | string, strictSource?: MetaSource | ValidationOptions, validationOptions?: ValidationOptions) | ||
``` | ||
## Examples | ||
@@ -166,0 +214,0 @@ ### FileSystemStoredFile storage configuration |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
79128
107
812
270
13
+ Addedfile-type@^16.5.4
+ Added@tokenizer/token@0.3.0(transitive)
+ Addedabort-controller@3.0.0(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbuffer@6.0.3(transitive)
+ Addedevent-target-shim@5.0.1(transitive)
+ Addedevents@3.3.0(transitive)
+ Addedfile-type@16.5.4(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedpeek-readable@4.1.0(transitive)
+ Addedprocess@0.11.10(transitive)
+ Addedreadable-stream@4.7.0(transitive)
+ Addedreadable-web-to-node-stream@3.0.4(transitive)
+ Addedstrtok3@6.3.0(transitive)
+ Addedtoken-types@4.2.1(transitive)