#nestjs #busboy #enoent
#nestjs #помощник официанта #enoent
Вопрос:
Я работаю с устаревшим проектом, в котором файлы MP4 загружаются один за другим из приложения React с axios
в NestJS API с busboy
на экземпляре EC2. Затем файл загружается в корзину S3.
Когда библиотека AWS S3 пытается загрузить файл, иногда возникает очевидная «случайная» ошибка:
warn
module: videoservice
method: saveTostream
message: Error on Upload Success callback
{ [Error: ENOENT: no such file or directory, open 'file.mp4'] errno: -2, code: 'ENOENT', syscall: 'open', path: 'file.mp4' }
(node:26886) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open 'file.mp4'
(node:26886) UnhandledPromiseRejectionwarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catc h(). (rejection id: 20)
Вот фрагменты:
Конечная точка загрузки API
@Post('upload/:id')
@ApiOperation({ ... })
@ApiResponse({ status: 201, type: Video })
@ApiConsumes('multipart/form-data')
@ApiImplicitFile({ name: 'file', required: true })
@ApiCreatedResponse({ type: UploadResponseDto })
async upload(
@Decoded() decoded: any,
@Param('id') videoId: number,
@Query('fileSize') fileSize :number,
@Req() req: Request,
@Res() res: Response,
@Body() uploadDto: UploadDto,
): Promise<any> {
try {
const busboy = new Busboy({
headers: req.headers,
});
const data = new Map();
const writtenFiles = [];
let upload;
busboy.on('field', (fieldname, val) => {
data.set(fieldname, val);
});
busboy.on(
'file',
async (fieldname, file, filename, encoding, mimetype) => {
try {
data.set('filename', filename);
data.set('mimetype', mimetype);
data.set('encoding', encoding);
const filepath = path.join(fieldname path.extname(filename));
upload = filepath;
const writeStream = fs.createWriteStream(filepath);
file.pipe(writeStream);
const promise = new Promise((resolve, reject) => {
file.on('end', () => {
writeStream.end();
});
writeStream.on('finish', resolve);
writeStream.on('error', reject);
});
writtenFiles.push(promise);
} catch (err) {
this.logger.error(log('busboy on file', err.message));
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
message: err.message,
});
}
},
);
busboy.on('error', err => {
this.logger.warn(log('busboy on error', err.message));
});
busboy.on('finish', async () => {
await Promise.all(writtenFiles);
res.status(HttpStatus.CREATED).send('OK');
await this.bucketService.saveStream( // Next snippet
fs.createReadStream(upload),
data.get('filename'),
data.get('mimetype'),
data.get('fileSize'),
data.get('programId'),
data.get('videoId')
);
return;
fs.unlinkSync(upload);
});
req.pipe(busboy);
} catch (err) {
this.logger.error(log('catch', err.message));
return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({
statusCode: HttpStatus.INTERNAL_SERVER_ERROR,
message: err.message,
});
}
}
метод BucketService saveStream
public async saveStream(
body: any
filename: string,
mimeType: string,
fileSize: number,
programId: number,
videoId: number
): Promise<any> {
try {
const callback = async (err: any, data: any) => {
if (err) {
this.logger.warn(log('Error on Upload Success callback', err));
throw err; // Here is where the error is raised
}
};
return this.s3
.upload(
this.setParams(body, filename, mimeType, programId, videoId),
(err, data) => callback(err, data),
)
} catch (err) {
this.logger.error(log('Error on S3 Upload', err));
throw err;
}
}
private setParams(
file: any,
filename: string,
mimeType: string,
programId: number,
videoId: number
): any {
return {
...awsBucketConfig,
Key: `${AWS_UPLOAD_DIR}${programId}/${videoId}/${Date.now()}${path.extname(
filename,
)}`,
Body: file,
ContentType: mimeType,
};
}
В какой-то момент я подумал, что, возможно, это происходит потому, что имя временного файла в EC2 всегда одно и то же: file.mp4
, и когда два файла загружаются одновременно, первый по завершении удаляет файл ( fs.unlinkSync(upload);
на конечной точке), оставляя другой текущий процесс без него, поэтому при попытке загрузить его этот процесс его не найдет. Но это неверно, потому что я выполнил тесты, в которых я гарантировал, что файлы были загружены один за другим. Однако я также убедился, что имя всегда было другим, изменив его на контроллере:
const filepath = path.join(fieldname path.extname(filename));
Автор:
const filepath = path.join(Math.floor(Math.random() * 10000) path.extname(filename));
но ошибка все еще происходит. Еще одна странная вещь, которая происходит, заключается в том, что на моем компьютере я могу видеть ( ls
) файл, пока он загружен, но в EC2 нет.
Факты:
-
EC2: t2.xl большого размера (около 4 ГБ свободного места)
-
ОС: Ubuntu 18
-
Версия узла: 10.21.0
-
Средний размер файла: 2 ГБ
-
Зависимости
"archiver": "^3.1.1", "async-busboy": "^0.7.0", "aws-sdk": "^2.553.0", "axios": "^0.19.0", "body-parser": "^1.19.0", "busboy": "^0.3.1",