Skip to content

Commit

Permalink
feat(Google Cloud Storage Node): Use streaming for file uploads (#6462)
Browse files Browse the repository at this point in the history
fix(Google Cloud Storage Node): Use streaming for file uploads
  • Loading branch information
netroy authored Jul 19, 2023
1 parent c7b74c3 commit cd0e41a
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ function convertN8nRequestToAxios(n8nRequest: IHttpRequestOptions): AxiosRequest
// Destructure properties with the same name first.
const { headers, method, timeout, auth, proxy, url } = n8nRequest;

const axiosRequest = {
const axiosRequest: AxiosRequestConfig = {
headers: headers ?? {},
method,
timeout,
Expand Down
30 changes: 22 additions & 8 deletions packages/nodes-base/nodes/Google/CloudStorage/ObjectDescription.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import FormData from 'form-data';
import type { IDataObject, INodeExecutionData, INodeProperties } from 'n8n-workflow';
import type { Readable } from 'stream';
import {
BINARY_ENCODING,
type IDataObject,
type INodeExecutionData,
type INodeProperties,
} from 'n8n-workflow';

// Define these because we'll be using them in two separate places
const metagenerationFilters: INodeProperties[] = [
Expand Down Expand Up @@ -144,23 +150,31 @@ export const objectOperations: INodeProperties[] = [
});

// Determine content and content type
let content: string | Buffer;
let content: string | Buffer | Readable;
let contentType: string;
let contentLength: number;
if (useBinary) {
const binaryPropertyName = this.getNodeParameter(
'createBinaryPropertyName',
) as string;

const binaryData = this.helpers.assertBinaryData(binaryPropertyName);

// Decode from base64 for upload
content = Buffer.from(binaryData.data, 'base64');
contentType = binaryData.mimeType;
if (binaryData.id) {
content = this.helpers.getBinaryStream(binaryData.id);
const binaryMetadata = await this.helpers.getBinaryMetadata(binaryData.id);
contentType = binaryMetadata.mimeType ?? 'application/octet-stream';
contentLength = binaryMetadata.fileSize;
} else {
content = Buffer.from(binaryData.data, BINARY_ENCODING);
contentType = binaryData.mimeType;
contentLength = content.length;
}
} else {
content = this.getNodeParameter('createContent') as string;
contentType = 'text/plain';
contentLength = content.length;
}
body.append('file', content, { contentType });
body.append('file', content, { contentType, knownLength: contentLength });

// Set the headers
if (!requestOptions.headers) requestOptions.headers = {};
Expand All @@ -170,7 +184,7 @@ export const objectOperations: INodeProperties[] = [
] = `multipart/related; boundary=${body.getBoundary()}`;

// Return the request data
requestOptions.body = body.getBuffer();
requestOptions.body = body;
return requestOptions;
},
],
Expand Down
1 change: 1 addition & 0 deletions packages/workflow/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"crypto-js": "^4.1.1",
"deep-equal": "^2.2.0",
"esprima-next": "5.8.4",
"form-data": "^4.0.0",
"jmespath": "^0.16.0",
"js-base64": "^3.7.2",
"lodash": "^4.17.21",
Expand Down
2 changes: 1 addition & 1 deletion packages/workflow/src/Interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// eslint-disable-next-line max-classes-per-file
import type * as express from 'express';
import type * as FormData from 'form-data';
import type FormData from 'form-data';
import type { IncomingHttpHeaders } from 'http';
import type { Readable } from 'stream';
import type { URLSearchParams } from 'url';
Expand Down
2 changes: 2 additions & 0 deletions packages/workflow/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import FormData from 'form-data';
import type { BinaryFileType, JsonObject } from './Interfaces';

const readStreamClasses = new Set(['ReadStream', 'Readable', 'ReadableStream']);

export const isObjectEmpty = (obj: object | null | undefined): boolean => {
if (obj === undefined || obj === null) return true;
if (typeof obj === 'object') {
if (obj instanceof FormData) return obj.getLengthSync() === 0;
if (Array.isArray(obj)) return obj.length === 0;
if (obj instanceof Set || obj instanceof Map) return obj.size === 0;
if (ArrayBuffer.isView(obj) || obj instanceof ArrayBuffer) return obj.byteLength === 0;
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit cd0e41a

Please sign in to comment.