Files
FastGPT/packages/global/common/file/tools.ts
T
Archer 36d1ff3679 feat: custom domain (#6067)
* perf: faq

* index

* delete dataset

* delete dataset

* perf: delete dataset

* init

* fix: faq

* doc

* fix: share link auth (#6063)

* standard plan add custom domain config (#6061)

* standard plan add custom domain config

* bill detail modal

* perf: vector count api

* feat: custom domain & wecom bot SaaS integration (#6047)

* feat: custom Domain type define

* feat: custom domain

* feat: wecom custom domain

* chore: i18n

* chore: i18n; team auth

* feat: wecom multi-model message support

* chore: wecom edit modal

* chore(doc): custom domain && wecom bot

* fix: type

* fix: type

* fix: file detect

* feat: fe

* fix: img name

* fix: test

* compress img

* rename

* editor initial status

* fix: chat url

* perf: s3 upload by buffer

* img

* refresh

* fix: custom domain selector (#6069)

* empty tip

* perf: s3 init

* sort provider

* fix: extend

* perf: extract filename

---------

Co-authored-by: Roy <whoeverimf5@gmail.com>
Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
2025-12-09 23:33:32 +08:00

91 lines
2.6 KiB
TypeScript

import { detect } from 'jschardet';
import { imageFileType } from './constants';
import { ChatFileTypeEnum } from '../../core/chat/constants';
import { type UserChatItemFileItemType } from '../../core/chat/type';
import * as fs from 'fs';
export const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
export const detectFileEncoding = (buffer: Buffer) => {
return detect(buffer.slice(0, 200))?.encoding?.toLocaleLowerCase();
};
export const detectFileEncodingByPath = async (path: string) => {
// Get 64KB file head
const MAX_BYTES = 64 * 1024;
const buffer = Buffer.alloc(MAX_BYTES);
const fd = await fs.promises.open(path, 'r');
try {
// Read file head
// @ts-ignore
const { bytesRead } = await fd.read(buffer, 0, MAX_BYTES, 0);
const actualBuffer = buffer.slice(0, bytesRead);
return detect(actualBuffer)?.encoding?.toLocaleLowerCase();
} finally {
await fd.close();
}
};
// Url => user upload file type
export const parseUrlToFileType = (url: string): UserChatItemFileItemType | undefined => {
if (typeof url !== 'string') return;
// Handle base64 image
if (url.startsWith('data:')) {
const matches = url.match(/^data:([^;]+);base64,/);
if (!matches) return;
const mimeType = matches[1].toLowerCase();
if (!mimeType.startsWith('image/')) return;
const extension = mimeType.split('/')[1];
return {
type: ChatFileTypeEnum.image,
name: `image.${extension}`,
url
};
}
try {
const parseUrl = new URL(url, 'http://localhost:3000');
// Get filename from URL
const filename = (() => {
// Here is a S3 Object Key
if (url.startsWith('chat/')) return url.split('/').pop()?.split('-')[1];
return parseUrl.searchParams.get('filename') || parseUrl.pathname.split('/').pop();
})();
const extension = filename?.split('.').pop()?.toLowerCase() || '';
if (extension && imageFileType.includes(extension)) {
// Default to file type for non-extension files
return {
type: ChatFileTypeEnum.image,
name: filename || 'null',
url
};
}
// If it's a document type, return as file, otherwise treat as image
return {
type: ChatFileTypeEnum.file,
name: filename || 'null',
url
};
} catch (error) {
return {
type: ChatFileTypeEnum.file,
name: url,
url
};
}
};