fix(Read Binary File Node): Do not crash the execution when the source file does not exist (#5100)

This issue was introduced in https://github.com/n8n-io/n8n/pull/5069
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-01-06 14:15:46 +01:00 committed by GitHub
parent c4df2049a8
commit c97f3cad59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 40 deletions

View file

@ -67,6 +67,7 @@ import {
ITriggerFunctions,
IWebhookFunctions,
BinaryMetadata,
FileSystemHelperFunctions,
} from 'n8n-workflow';
import { Agent } from 'https';
@ -93,6 +94,8 @@ import axios, {
} from 'axios';
import url, { URL, URLSearchParams } from 'url';
import type { Readable } from 'stream';
import { access as fsAccess } from 'fs/promises';
import { createReadStream } from 'fs';
import { BinaryDataManager } from './BinaryDataManager';
import type { IResponseError, IWorkflowSettings } from './Interfaces';
@ -1997,6 +2000,21 @@ const getRequestHelperFunctions = (
},
});
const getFileSystemHelperFunctions = (node: INode): FileSystemHelperFunctions => ({
async createReadStream(filePath) {
try {
await fsAccess(filePath);
} catch (error) {
throw error.code === 'ENOENT'
? new NodeOperationError(node, error, {
message: `The file "${String(filePath)}" could not be accessed.`,
})
: error;
}
return createReadStream(filePath);
},
});
const getBinaryHelperFunctions = ({
executionId,
}: IWorkflowExecuteAdditionalData): BinaryHelperFunctions => ({
@ -2292,6 +2310,7 @@ export function getExecuteFunctions(
},
helpers: {
...getRequestHelperFunctions(workflow, node, additionalData),
...getFileSystemHelperFunctions(node),
...getBinaryHelperFunctions(additionalData),
getBinaryDataBuffer: async (itemIndex, propertyName, inputIndex = 0) =>
getBinaryDataBuffer(inputData, itemIndex, propertyName, inputIndex),

View file

@ -1,12 +1,5 @@
import { IExecuteFunctions } from 'n8n-core';
import {
INodeExecutionData,
INodeType,
INodeTypeDescription,
NodeOperationError,
} from 'n8n-workflow';
import { createReadStream } from 'fs';
import type { IExecuteFunctions } from 'n8n-core';
import type { INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
export class ReadBinaryFile implements INodeType {
description: INodeTypeDescription = {
@ -53,23 +46,6 @@ export class ReadBinaryFile implements INodeType {
for (let itemIndex = 0; itemIndex < length; itemIndex++) {
try {
item = items[itemIndex];
const dataPropertyName = this.getNodeParameter('dataPropertyName', itemIndex) as string;
const filePath = this.getNodeParameter('filePath', itemIndex) as string;
let data;
try {
data = createReadStream(filePath);
} catch (error) {
if (error.code === 'ENOENT') {
throw new NodeOperationError(
this.getNode(),
`The file "${filePath}" could not be found.`,
);
}
throw error;
}
const newItem: INodeExecutionData = {
json: item.json,
binary: {},
@ -85,7 +61,10 @@ export class ReadBinaryFile implements INodeType {
Object.assign(newItem.binary, item.binary);
}
newItem.binary![dataPropertyName] = await this.helpers.prepareBinaryData(data, filePath);
const filePath = this.getNodeParameter('filePath', itemIndex) as string;
const stream = await this.helpers.createReadStream(filePath);
const dataPropertyName = this.getNodeParameter('dataPropertyName', itemIndex) as string;
newItem.binary![dataPropertyName] = await this.helpers.prepareBinaryData(stream, filePath);
returnData.push(newItem);
} catch (error) {
if (this.continueOnFail()) {

View file

@ -1,8 +1,6 @@
import { IExecuteFunctions } from 'n8n-core';
import { INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
import type { IExecuteFunctions } from 'n8n-core';
import type { INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
import glob from 'fast-glob';
import { createReadStream } from 'fs';
import type { Readable } from 'stream';
export class ReadBinaryFiles implements INodeType {
description: INodeTypeDescription = {
@ -46,22 +44,17 @@ export class ReadBinaryFiles implements INodeType {
const files = await glob(fileSelector);
const items: INodeExecutionData[] = [];
let item: INodeExecutionData;
let data: Readable;
for (const filePath of files) {
data = createReadStream(filePath);
item = {
const stream = await this.helpers.createReadStream(filePath);
items.push({
binary: {
[dataPropertyName]: await this.helpers.prepareBinaryData(data, filePath),
[dataPropertyName]: await this.helpers.prepareBinaryData(stream, filePath),
},
json: {},
pairedItem: {
item: 0,
},
};
items.push(item);
});
}
return this.prepareOutputData(items);

View file

@ -15,6 +15,7 @@ import type { WorkflowActivationError } from './WorkflowActivationError';
import type { WorkflowOperationError } from './WorkflowErrors';
import type { NodeApiError, NodeOperationError } from './NodeErrors';
import type { ExpressionError } from './ExpressionError';
import { PathLike } from 'fs';
export interface IAdditionalCredentialOptions {
oauth2?: IOAuth2Options;
@ -640,6 +641,10 @@ export interface JsonHelperFunctions {
returnJsonArray(jsonData: IDataObject | IDataObject[]): INodeExecutionData[];
}
export interface FileSystemHelperFunctions {
createReadStream(path: PathLike): Promise<Readable>;
}
export interface BinaryHelperFunctions {
prepareBinaryData(
binaryData: Buffer | Readable,
@ -725,6 +730,7 @@ export type IExecuteFunctions = ExecuteFunctions.GetNodeParameterFn &
helpers: RequestHelperFunctions &
BinaryHelperFunctions &
FileSystemHelperFunctions &
JsonHelperFunctions & {
normalizeItems(items: INodeExecutionData | INodeExecutionData[]): INodeExecutionData[];
constructExecutionMetaData(

View file

@ -217,6 +217,7 @@ abstract class NodeError extends ExecutionBaseError {
}
interface NodeOperationErrorOptions {
message?: string;
description?: string;
runIndex?: number;
itemIndex?: number;
@ -234,6 +235,9 @@ export class NodeOperationError extends NodeError {
}
super(node, error);
if (options.message) {
this.message = options.message;
}
this.description = options.description;
this.context.runIndex = options.runIndex;
this.context.itemIndex = options.itemIndex;