refactor(core): Reduce memory usage in the Webhook node (#4640)
use file streaming to pass webhook binaries around
This commit is contained in:
committed by
GitHub
parent
602b1e56d6
commit
07e4743a3e
@@ -20,10 +20,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
IBinaryData,
|
||||
IRunData,
|
||||
} from 'n8n-workflow';
|
||||
import type { IBinaryData, IRunData } from 'n8n-workflow';
|
||||
|
||||
import BinaryDataDisplayEmbed from '@/components/BinaryDataDisplayEmbed.vue';
|
||||
|
||||
@@ -44,7 +41,7 @@ export default mixins(
|
||||
BinaryDataDisplayEmbed,
|
||||
},
|
||||
props: [
|
||||
'displayData', // IBinaryDisplayData
|
||||
'displayData', // IBinaryData
|
||||
'windowVisible', // boolean
|
||||
],
|
||||
computed: {
|
||||
@@ -67,14 +64,6 @@ export default mixins(
|
||||
return binaryDataItem;
|
||||
},
|
||||
|
||||
embedClass (): string[] {
|
||||
// @ts-ignore
|
||||
if (this.binaryData! !== null && this.binaryData!.mimeType! !== undefined && (this.binaryData!.mimeType! as string).startsWith('image')) {
|
||||
return ['image'];
|
||||
}
|
||||
return ['other'];
|
||||
},
|
||||
|
||||
workflowRunData (): IRunData | null {
|
||||
const workflowExecution = this.workflowsStore.getWorkflowExecution;
|
||||
if (workflowExecution === null) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
Error loading binary data
|
||||
</div>
|
||||
<span v-else>
|
||||
<video v-if="binaryData.mimeType && binaryData.mimeType.startsWith('video/')" controls autoplay>
|
||||
<video v-if="binaryData.fileType === 'video'" controls autoplay>
|
||||
<source :src="embedSource" :type="binaryData.mimeType">
|
||||
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
|
||||
</video>
|
||||
@@ -17,10 +17,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
||||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
import { restApi } from '@/mixins/restApi';
|
||||
import type { IBinaryData } from 'n8n-workflow';
|
||||
|
||||
export default mixins(
|
||||
restApi,
|
||||
@@ -28,7 +27,7 @@ export default mixins(
|
||||
.extend({
|
||||
name: 'BinaryDataDisplayEmbed',
|
||||
props: [
|
||||
'binaryData', // IBinaryDisplayData
|
||||
'binaryData', // IBinaryData
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
@@ -38,15 +37,15 @@ export default mixins(
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
if(!this.binaryData.id) {
|
||||
const id = this.binaryData?.id;
|
||||
if(!id) {
|
||||
this.embedSource = 'data:' + this.binaryData.mimeType + ';base64,' + this.binaryData.data;
|
||||
this.isLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const bufferString = await this.restApi().getBinaryBufferString(this.binaryData!.id!);
|
||||
this.embedSource = 'data:' + this.binaryData.mimeType + ';base64,' + bufferString;
|
||||
this.embedSource = this.restApi().getBinaryUrl(id);
|
||||
this.isLoading = false;
|
||||
} catch (e) {
|
||||
this.isLoading = false;
|
||||
@@ -55,11 +54,8 @@ export default mixins(
|
||||
},
|
||||
methods: {
|
||||
embedClass(): string[] {
|
||||
// @ts-ignore
|
||||
if (this.binaryData! !== null && this.binaryData!.mimeType! !== undefined && (this.binaryData!.mimeType! as string).startsWith('image')) {
|
||||
return ['image'];
|
||||
}
|
||||
return ['other'];
|
||||
const { fileType } = (this.binaryData || {}) as IBinaryData;
|
||||
return [fileType ?? 'other'];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -282,9 +282,13 @@
|
||||
<div><n8n-text size="small" :bold="true">{{ $locale.baseText('runData.mimeType') }}: </n8n-text></div>
|
||||
<div :class="$style.binaryValue">{{binaryData.mimeType}}</div>
|
||||
</div>
|
||||
<div v-if="binaryData.fileSize">
|
||||
<div><n8n-text size="small" :bold="true">{{ $locale.baseText('runData.fileSize') }}: </n8n-text></div>
|
||||
<div :class="$style.binaryValue">{{binaryData.fileSize}}</div>
|
||||
</div>
|
||||
|
||||
<div :class="$style.binaryButtonContainer">
|
||||
<n8n-button size="small" :label="$locale.baseText('runData.showBinaryData')" class="binary-data-show-data-button" @click="displayBinaryData(index, key)" />
|
||||
<n8n-button v-if="isViewable(index, key)" size="small" :label="$locale.baseText('runData.showBinaryData')" class="binary-data-show-data-button" @click="displayBinaryData(index, key)" />
|
||||
<n8n-button v-if="isDownloadable(index, key)" size="small" type="secondary" :label="$locale.baseText('runData.downloadBinaryData')" class="binary-data-show-data-button" @click="downloadBinaryData(index, key)" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -341,7 +345,6 @@ import {
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import {
|
||||
IBinaryDisplayData,
|
||||
IExecutionResponse,
|
||||
INodeUi,
|
||||
INodeUpdatePropertiesInformation,
|
||||
@@ -363,7 +366,6 @@ import BinaryDataDisplay from '@/components/BinaryDataDisplay.vue';
|
||||
import WarningTooltip from '@/components/WarningTooltip.vue';
|
||||
import NodeErrorView from '@/components/Error/NodeErrorView.vue';
|
||||
|
||||
import { copyPaste } from '@/mixins/copyPaste';
|
||||
import { externalHooks } from "@/mixins/externalHooks";
|
||||
import { genericHelpers } from '@/mixins/genericHelpers';
|
||||
import { nodeHelpers } from '@/mixins/nodeHelpers';
|
||||
@@ -385,7 +387,6 @@ export type EnterEditModeArgs = {
|
||||
};
|
||||
|
||||
export default mixins(
|
||||
copyPaste,
|
||||
externalHooks,
|
||||
genericHelpers,
|
||||
nodeHelpers,
|
||||
@@ -460,7 +461,7 @@ export default mixins(
|
||||
showData: false,
|
||||
outputIndex: 0,
|
||||
binaryDataDisplayVisible: false,
|
||||
binaryDataDisplayData: null as IBinaryDisplayData | null,
|
||||
binaryDataDisplayData: null as IBinaryData | null,
|
||||
|
||||
MAX_DISPLAY_DATA_SIZE,
|
||||
MAX_DISPLAY_ITEMS_AUTO_ALL,
|
||||
@@ -1041,23 +1042,26 @@ export default mixins(
|
||||
this.workflowsStore.setWorkflowExecutionData(null);
|
||||
this.updateNodesExecutionIssues();
|
||||
},
|
||||
isViewable (index: number, key: string): boolean {
|
||||
const { fileType }: IBinaryData = this.binaryData[index][key];
|
||||
return !!fileType && ['image', 'video'].includes(fileType);
|
||||
},
|
||||
isDownloadable (index: number, key: string): boolean {
|
||||
const binaryDataItem: IBinaryData = this.binaryData[index][key];
|
||||
return !!(binaryDataItem.mimeType && binaryDataItem.fileName);
|
||||
const { mimeType, fileName }: IBinaryData = this.binaryData[index][key];
|
||||
return !!(mimeType && fileName);
|
||||
},
|
||||
async downloadBinaryData (index: number, key: string) {
|
||||
const binaryDataItem: IBinaryData = this.binaryData[index][key];
|
||||
const { id, data, fileName, fileExtension, mimeType }: IBinaryData = this.binaryData[index][key];
|
||||
|
||||
let bufferString = 'data:' + binaryDataItem.mimeType + ';base64,';
|
||||
if(binaryDataItem.id) {
|
||||
bufferString += await this.restApi().getBinaryBufferString(binaryDataItem.id);
|
||||
if(id) {
|
||||
const url = this.restApi().getBinaryUrl(id);
|
||||
saveAs(url, [fileName, fileExtension].join('.'));
|
||||
return;
|
||||
} else {
|
||||
bufferString += binaryDataItem.data;
|
||||
const bufferString = 'data:' + mimeType + ';base64,' + data;
|
||||
const blob = await fetch(bufferString).then(d => d.blob());
|
||||
saveAs(blob, fileName);
|
||||
}
|
||||
|
||||
const data = await fetch(bufferString);
|
||||
const blob = await data.blob();
|
||||
saveAs(blob, binaryDataItem.fileName);
|
||||
},
|
||||
displayBinaryData (index: number, key: string) {
|
||||
this.binaryDataDisplayVisible = true;
|
||||
|
||||
Reference in New Issue
Block a user