mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
fix(Move Binary Data Node): Stringify objects before encoding them in MoveBinaryData (#4882)
* stringify objects before encoding them objects in MoveBinaryData * add fileSize and fileType on MoveBinaryData converted data * show `view` option for text files as well * improve how JSON binary data is shown in the UI
This commit is contained in:
parent
f4481e24e8
commit
3b969d2cd1
|
@ -64,7 +64,7 @@ import {
|
|||
NodeExecutionWithMetadata,
|
||||
IPairedItemData,
|
||||
deepCopy,
|
||||
BinaryFileType,
|
||||
fileTypeFromMimeType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { Agent } from 'https';
|
||||
|
@ -836,13 +836,6 @@ export async function getBinaryDataBuffer(
|
|||
return BinaryDataManager.getInstance().retrieveBinaryData(binaryData);
|
||||
}
|
||||
|
||||
function fileTypeFromMimeType(mimeType: string): BinaryFileType | undefined {
|
||||
if (mimeType.startsWith('image/')) return 'image';
|
||||
if (mimeType.startsWith('video/')) return 'video';
|
||||
if (mimeType.startsWith('text/') || mimeType.startsWith('application/json')) return 'text';
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an incoming IBinaryData & related buffer using the configured binary data manager.
|
||||
*
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-if="windowVisible" class="binary-data-window">
|
||||
<div v-if="windowVisible" :class="['binary-data-window', binaryData?.fileType]">
|
||||
<n8n-button
|
||||
@click.stop="closeWindow"
|
||||
size="small"
|
||||
|
@ -98,6 +98,10 @@ export default mixins(
|
|||
overflow: hidden;
|
||||
text-align: center;
|
||||
|
||||
&.json {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.binary-data-window-wrapper {
|
||||
margin-top: .5em;
|
||||
padding: 0 1em;
|
||||
|
|
|
@ -11,6 +11,12 @@
|
|||
<source :src="embedSource" :type="binaryData.mimeType">
|
||||
{{ $locale.baseText('binaryDataDisplay.yourBrowserDoesNotSupport') }}
|
||||
</video>
|
||||
<vue-json-pretty
|
||||
v-else-if="binaryData.fileType === 'json'"
|
||||
:data="jsonData"
|
||||
:deep="3"
|
||||
:showLength="true"
|
||||
/>
|
||||
<embed v-else :src="embedSource" class="binary-data" :class="embedClass()"/>
|
||||
</span>
|
||||
</span>
|
||||
|
@ -19,38 +25,56 @@
|
|||
<script lang="ts">
|
||||
import mixins from 'vue-typed-mixins';
|
||||
import { restApi } from '@/mixins/restApi';
|
||||
import type { IBinaryData } from 'n8n-workflow';
|
||||
import { IBinaryData, jsonParse } from 'n8n-workflow';
|
||||
import type { PropType } from 'vue';
|
||||
import VueJsonPretty from 'vue-json-pretty';
|
||||
|
||||
export default mixins(
|
||||
restApi,
|
||||
)
|
||||
.extend({
|
||||
name: 'BinaryDataDisplayEmbed',
|
||||
props: [
|
||||
'binaryData', // IBinaryData
|
||||
],
|
||||
components: {
|
||||
VueJsonPretty,
|
||||
},
|
||||
props: {
|
||||
binaryData: {
|
||||
type: Object as PropType<IBinaryData>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: true,
|
||||
embedSource: '',
|
||||
error: false,
|
||||
jsonData: '',
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
const id = this.binaryData?.id;
|
||||
const isJSONData = this.binaryData.fileType === 'json';
|
||||
|
||||
if(!id) {
|
||||
this.embedSource = 'data:' + this.binaryData.mimeType + ';base64,' + this.binaryData.data;
|
||||
this.isLoading = false;
|
||||
return;
|
||||
if (isJSONData) {
|
||||
this.jsonData = jsonParse(atob(this.binaryData.data));
|
||||
} else {
|
||||
this.embedSource = 'data:' + this.binaryData.mimeType + ';base64,' + this.binaryData.data;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
const binaryUrl = this.restApi().getBinaryUrl(id);
|
||||
if (isJSONData) {
|
||||
this.jsonData = await (await fetch(binaryUrl)).json();
|
||||
} else {
|
||||
this.embedSource = binaryUrl;
|
||||
}
|
||||
} catch (e) {
|
||||
this.error = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.embedSource = this.restApi().getBinaryUrl(id);
|
||||
this.isLoading = false;
|
||||
} catch (e) {
|
||||
this.isLoading = false;
|
||||
this.error = true;
|
||||
}
|
||||
this.isLoading = false;
|
||||
},
|
||||
methods: {
|
||||
embedClass(): string[] {
|
||||
|
|
|
@ -1060,15 +1060,15 @@ export default mixins(
|
|||
this.updateNodesExecutionIssues();
|
||||
},
|
||||
isViewable (index: number, key: string): boolean {
|
||||
const { fileType }: IBinaryData = this.binaryData[index][key];
|
||||
return !!fileType && ['image', 'video'].includes(fileType);
|
||||
const { fileType } = this.binaryData[index][key];
|
||||
return !!fileType && ['image', 'video', 'text', 'json'].includes(fileType);
|
||||
},
|
||||
isDownloadable (index: number, key: string): boolean {
|
||||
const { mimeType, fileName }: IBinaryData = this.binaryData[index][key];
|
||||
const { mimeType, fileName } = this.binaryData[index][key];
|
||||
return !!(mimeType && fileName);
|
||||
},
|
||||
async downloadBinaryData (index: number, key: string) {
|
||||
const { id, data, fileName, fileExtension, mimeType }: IBinaryData = this.binaryData[index][key];
|
||||
const { id, data, fileName, fileExtension, mimeType } = this.binaryData[index][key];
|
||||
|
||||
if(id) {
|
||||
const url = this.restApi().getBinaryUrl(id);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { get, set, unset } from 'lodash';
|
||||
import prettyBytes from 'pretty-bytes';
|
||||
|
||||
import { BINARY_ENCODING, IExecuteFunctions } from 'n8n-core';
|
||||
|
||||
|
@ -12,6 +13,7 @@ import {
|
|||
INodeTypeDescription,
|
||||
jsonParse,
|
||||
NodeOperationError,
|
||||
fileTypeFromMimeType,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import iconv from 'iconv-lite';
|
||||
|
@ -415,20 +417,26 @@ export class MoveBinaryData implements INodeType {
|
|||
newItem.binary = {};
|
||||
}
|
||||
|
||||
const mimeType = (options.mimeType as string) || 'application/json';
|
||||
const convertedValue: IBinaryData = {
|
||||
data: '',
|
||||
mimeType,
|
||||
fileType: fileTypeFromMimeType(mimeType),
|
||||
};
|
||||
|
||||
if (options.dataIsBase64 !== true) {
|
||||
if (options.useRawData !== true) {
|
||||
if (options.useRawData !== true || typeof value === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
|
||||
value = iconv
|
||||
.encode(value as string, encoding, { addBOM: options.addBOM as boolean })
|
||||
.toString(BINARY_ENCODING);
|
||||
}
|
||||
convertedValue.fileSize = prettyBytes(value.length);
|
||||
|
||||
const convertedValue: IBinaryData = {
|
||||
data: value as string,
|
||||
mimeType: (options.mimeType as string) || 'application/json',
|
||||
};
|
||||
convertedValue.data = iconv
|
||||
.encode(value, encoding, { addBOM: options.addBOM as boolean })
|
||||
.toString(BINARY_ENCODING);
|
||||
} else {
|
||||
convertedValue.data = value as unknown as string;
|
||||
}
|
||||
|
||||
if (options.fileName) {
|
||||
convertedValue.fileName = options.fileName as string;
|
||||
|
|
|
@ -797,6 +797,7 @@
|
|||
"pdf-parse": "^1.1.1",
|
||||
"pg": "^8.3.0",
|
||||
"pg-promise": "^10.5.8",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"promise-ftp": "^1.3.5",
|
||||
"redis": "^3.1.1",
|
||||
"request": "^2.88.2",
|
||||
|
|
|
@ -27,7 +27,7 @@ export type IAllExecuteFunctions =
|
|||
| ITriggerFunctions
|
||||
| IWebhookFunctions;
|
||||
|
||||
export type BinaryFileType = 'text' | 'image' | 'video';
|
||||
export type BinaryFileType = 'text' | 'json' | 'image' | 'video';
|
||||
export interface IBinaryData {
|
||||
[key: string]: string | undefined;
|
||||
data: string;
|
||||
|
|
|
@ -18,7 +18,7 @@ export * from './WorkflowErrors';
|
|||
export * from './WorkflowHooks';
|
||||
export * from './VersionedNodeType';
|
||||
export { LoggerProxy, NodeHelpers, ObservableObject, TelemetryHelpers };
|
||||
export { deepCopy, jsonParse, sleep } from './utils';
|
||||
export { deepCopy, jsonParse, sleep, fileTypeFromMimeType } from './utils';
|
||||
export {
|
||||
isINodeProperties,
|
||||
isINodePropertyOptions,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { BinaryFileType } from './Interfaces';
|
||||
|
||||
export type Primitives = string | number | boolean | bigint | symbol | null | undefined;
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
|
||||
|
@ -64,3 +66,11 @@ export const sleep = async (ms: number): Promise<void> =>
|
|||
new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
|
||||
export function fileTypeFromMimeType(mimeType: string): BinaryFileType | undefined {
|
||||
if (mimeType.startsWith('application/json')) return 'json';
|
||||
if (mimeType.startsWith('image/')) return 'image';
|
||||
if (mimeType.startsWith('video/')) return 'video';
|
||||
if (mimeType.startsWith('text/')) return 'text';
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -738,6 +738,7 @@ importers:
|
|||
pdf-parse: ^1.1.1
|
||||
pg: ^8.3.0
|
||||
pg-promise: ^10.5.8
|
||||
pretty-bytes: ^5.6.0
|
||||
promise-ftp: ^1.3.5
|
||||
redis: ^3.1.1
|
||||
request: ^2.88.2
|
||||
|
@ -796,6 +797,7 @@ importers:
|
|||
pdf-parse: 1.1.1
|
||||
pg: 8.8.0
|
||||
pg-promise: 10.12.0
|
||||
pretty-bytes: 5.6.0
|
||||
promise-ftp: 1.3.5_promise-ftp-common@1.1.5
|
||||
redis: 3.1.2
|
||||
request: 2.88.2
|
||||
|
|
Loading…
Reference in a new issue