Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions modules/tool/packages/base64Decode/children/toFile/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { defineTool } from '@tool/type';
import { FlowNodeInputTypeEnum, WorkflowIOValueTypeEnum } from '@tool/type/fastgpt';
import { ToolTypeEnum } from '@tool/type/tool';

export default defineTool({
name: {
'zh-CN': 'Base64 转文件',
en: 'Base64 to File'
},
type: ToolTypeEnum.tools,
description: {
'zh-CN': '将 Base64 编码的字符串转换为文件。',
en: 'Enter a Base64-encoded string and get a file.'
},
toolDescription: 'Base64-encoded to file',
versionList: [
{
value: '0.1.0',
description: 'Default version',
inputs: [
{
key: 'base64',
label: 'Base64 字符串',
renderTypeList: [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.string,
required: true
}
],
outputs: [
{
valueType: WorkflowIOValueTypeEnum.string,
key: 'url',
label: '文件 URL',
description: '可访问的文件地址: http://example.com',
required: true
}
]
}
]
});
10 changes: 10 additions & 0 deletions modules/tool/packages/base64Decode/children/toFile/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import config from './config';
import { InputType, OutputType, tool as toolCb } from './src';
import { exportTool } from '@tool/utils/tool';

export default exportTool({
toolCb,
InputType,
OutputType,
config
});
117 changes: 117 additions & 0 deletions modules/tool/packages/base64Decode/children/toFile/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { z } from 'zod';
import { uploadFile } from '@tool/utils/uploadFile';

/**
* Detect image MIME type from base64 binary data by checking file signatures
* Supports pdf, docx, xlsx, pptx, zip, wav, avi formats
*/
function detectFileType(base64Data: string) {
try {
// Remove data URL prefix if exists and decode base64
const base64Content = base64Data.replace(/^data:[^;]+;base64,/, '');
const binaryString = atob(base64Content);
const bytes = new Uint8Array(binaryString.length);

for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}

// PDF: 25 50 44 46
if (
bytes.length >= 4 &&
bytes[0] === 0x25 &&
bytes[1] === 0x50 &&
bytes[2] === 0x44 &&
bytes[3] === 0x46
) {
return 'application/pdf';
}

// zip: 50 4B 03 04
// .docx, .xlsx, .pptx are special zip files
if (
bytes.length >= 4 &&
bytes[0] === 0x50 &&
bytes[1] === 0x4b &&
bytes[2] === 0x03 &&
bytes[3] === 0x4
) {
// detect specific file paths inside the ZIP to identify the specific Office type
// check the first 10000 bytes to see if it contains specific file paths
const text = binaryString.substring(0, 10000);

if (text.includes('word/')) {
return 'application/docx';
} else if (text.includes('xl/')) {
return 'application/xlsx';
} else if (text.includes('ppt/')) {
return 'application/pptx';
} else {
return 'application/zip';
}
}

// csv: check if it contains comma separated structure
if (/^[^\n]*,[^\n]*$/.test(binaryString.substring(0, 1000))) {
return 'text/csv';
}

// html: check if it includes <html> or <!doctype html>
if (
binaryString.substring(0, 100).toLowerCase().includes('<html') ||
binaryString.substring(0, 100).toLowerCase().includes('<!doctype html')
) {
return 'text/html';
}

// txt: check if it is pure ASCII text
if (/^[\x20-\x7E\s]*$/.test(binaryString.substring(0, 1000))) {
return 'text/txt';
}

return null;
} catch {
return null;
}
}

export const InputType = z.object({
base64: z.string().nonempty()
});

export const OutputType = z.object({
url: z.string()
});

export async function tool({
base64
}: z.infer<typeof InputType>): Promise<z.infer<typeof OutputType>> {
const mime = (() => {
const match = base64.match(/^data:([^;]+);base64,/);
if (match?.[1]) {
return match[1];
}
const detectedType = detectFileType(base64);

if (!detectedType) {
throw new Error(
'File Type unknown, current supported file types: pdf, docx, xlsx, pptx, zip, csv, html, txt'
);
}
return detectedType;
})();

const ext = (() => {
const m = mime.split('/')[1];
// octet-stream: unknown binary data
return m && m.length > 0 ? m : 'octet-stream';
})();

const filename = `file.${ext}`;

const meta = await uploadFile({ base64, defaultFilename: filename });

return {
url: meta.accessUrl
};
}