@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
112 lines (93 loc) • 3.04 kB
text/typescript
import { TRPCError } from '@trpc/server';
import { serverDBEnv } from '@/config/db';
import { FileModel } from '@/database/models/file';
import { FileItem } from '@/database/schemas';
import { LobeChatDatabase } from '@/database/type';
import { TempFileManager } from '@/server/utils/tempFileManager';
import { nanoid } from '@/utils/uuid';
import { FileServiceImpl, createFileServiceModule } from './impls';
/**
* 文件服务类
* 使用模块化实现方式,提供文件操作服务
*/
export class FileService {
private userId: string;
private fileModel: FileModel;
private impl: FileServiceImpl = createFileServiceModule();
constructor(db: LobeChatDatabase, userId: string) {
this.userId = userId;
this.fileModel = new FileModel(db, userId);
}
/**
* 删除文件
*/
public async deleteFile(key: string) {
return this.impl.deleteFile(key);
}
/**
* 批量删除文件
*/
public async deleteFiles(keys: string[]) {
return this.impl.deleteFiles(keys);
}
/**
* 获取文件内容
*/
public async getFileContent(key: string): Promise<string> {
return this.impl.getFileContent(key);
}
/**
* 获取文件字节数组
*/
public async getFileByteArray(key: string): Promise<Uint8Array> {
return this.impl.getFileByteArray(key);
}
/**
* 创建预签名上传URL
*/
public async createPreSignedUrl(key: string): Promise<string> {
return this.impl.createPreSignedUrl(key);
}
/**
* 创建预签名预览URL
*/
public async createPreSignedUrlForPreview(key: string, expiresIn?: number): Promise<string> {
return this.impl.createPreSignedUrlForPreview(key, expiresIn);
}
/**
* 上传内容
*/
public async uploadContent(path: string, content: string) {
return this.impl.uploadContent(path, content);
}
/**
* 获取完整文件URL
*/
public async getFullFileUrl(url?: string | null, expiresIn?: number): Promise<string> {
return this.impl.getFullFileUrl(url, expiresIn);
}
async downloadFileToLocal(
fileId: string,
): Promise<{ cleanup: () => void; file: FileItem; filePath: string }> {
const file = await this.fileModel.findById(fileId);
if (!file) {
throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
}
let content: Uint8Array | undefined;
try {
content = await this.getFileByteArray(file.url);
} catch (e) {
console.error(e);
// if file not found, delete it from db
if ((e as any).Code === 'NoSuchKey') {
await this.fileModel.delete(fileId, serverDBEnv.REMOVE_GLOBAL_FILE);
throw new TRPCError({ code: 'BAD_REQUEST', message: 'File not found' });
}
}
if (!content) throw new TRPCError({ code: 'BAD_REQUEST', message: 'File content is empty' });
const dir = nanoid();
const tempManager = new TempFileManager(dir);
const filePath = await tempManager.writeTempFile(content, file.name);
return { cleanup: () => tempManager.cleanup(), file, filePath };
}
}