feat: create file record via path (#5088)

This commit is contained in:
chenos 2024-08-19 21:55:02 +08:00 committed by GitHub
parent fc86ec5fa5
commit f3d15d5e8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 75 additions and 9 deletions

View File

@ -11,6 +11,7 @@ import { promises as fs } from 'fs';
import path from 'path';
import { getApp } from '.';
import { FILE_FIELD_NAME, FILE_SIZE_LIMIT_DEFAULT, STORAGE_TYPE_LOCAL } from '../../constants';
import PluginFileManagerServer from '../server';
const { LOCAL_STORAGE_BASE_URL, LOCAL_STORAGE_DEST = 'storage/uploads', APP_PORT = '13000' } = process.env;
@ -50,6 +51,23 @@ describe('action', () => {
describe('create / upload', () => {
describe('default storage', () => {
it('createFileRecord', async () => {
const Plugin = app.pm.get(PluginFileManagerServer) as PluginFileManagerServer;
const model = await Plugin.createFileRecord({
collection: 'attachments',
filePath: path.resolve(__dirname, './files/text.txt'),
});
const matcher = {
title: 'text',
extname: '.txt',
path: '',
// size: 13,
meta: {},
storageId: 1,
};
expect(model.toJSON()).toMatchObject(matcher);
});
it('upload file should be ok', async () => {
const { body } = await agent.resource('attachments').create({
[FILE_FIELD_NAME]: path.resolve(__dirname, './files/text.txt'),

View File

@ -11,15 +11,15 @@ import { Context, Next } from '@nocobase/actions';
import { koaMulter as multer } from '@nocobase/utils';
import Path from 'path';
import Plugin from '..';
import {
FILE_FIELD_NAME,
FILE_SIZE_LIMIT_DEFAULT,
FILE_SIZE_LIMIT_MAX,
FILE_FIELD_NAME,
LIMIT_FILES,
FILE_SIZE_LIMIT_MIN,
LIMIT_FILES,
} from '../../constants';
import * as Rules from '../rules';
import Plugin from '..';
// TODO(optimize): 需要优化错误处理,计算失败后需要抛出对应错误,以便程序处理
function getFileFilter(storage) {
@ -33,7 +33,7 @@ function getFileFilter(storage) {
};
}
function getFileData(ctx: Context) {
export function getFileData(ctx: Context) {
const { [FILE_FIELD_NAME]: file, storage } = ctx;
if (!file) {
return ctx.throw(400, 'file validation failed');

View File

@ -7,29 +7,77 @@
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { resolve } from 'path';
import { Plugin } from '@nocobase/server';
import { Registry } from '@nocobase/utils';
import { basename, resolve } from 'path';
import { Transactionable } from '@nocobase/database';
import fs from 'fs';
import { STORAGE_TYPE_ALI_OSS, STORAGE_TYPE_LOCAL, STORAGE_TYPE_S3, STORAGE_TYPE_TX_COS } from '../constants';
import { FileModel } from './FileModel';
import initActions from './actions';
import { getFileData } from './actions/attachments';
import { AttachmentInterface } from './interfaces/attachment-interface';
import { IStorage, StorageModel } from './storages';
import { STORAGE_TYPE_ALI_OSS, STORAGE_TYPE_LOCAL, STORAGE_TYPE_S3, STORAGE_TYPE_TX_COS } from '../constants';
import StorageTypeLocal from './storages/local';
import StorageTypeAliOss from './storages/ali-oss';
import StorageTypeLocal from './storages/local';
import StorageTypeS3 from './storages/s3';
import StorageTypeTxCos from './storages/tx-cos';
import { AttachmentInterface } from './interfaces/attachment-interface';
export type * from './storages';
const DEFAULT_STORAGE_TYPE = STORAGE_TYPE_LOCAL;
export type FileRecordOptions = {
collection: string;
filePath: string;
} & Transactionable;
export default class PluginFileManagerServer extends Plugin {
storageTypes = new Registry<IStorage>();
storagesCache = new Map<number, StorageModel>();
async createFileRecord(options: FileRecordOptions) {
const { collection, filePath, transaction } = options;
const storageRepository = this.db.getRepository('storages');
const collectionRepository = this.db.getRepository(collection);
const storage = await storageRepository.findOne();
const fileStream = fs.createReadStream(filePath);
if (!storage) {
throw new Error('[file-manager] no linked or default storage provided');
}
const storageConfig = this.storageTypes.get(storage.type);
if (!storageConfig) {
throw new Error(`[file-manager] storage type "${storage.type}" is not defined`);
}
const engine = storageConfig.make(storage);
const file = {
originalname: basename(filePath),
path: filePath,
stream: fileStream,
} as any;
await new Promise((resolve, reject) => {
engine._handleFile({} as any, file, (error, info) => {
if (error) {
reject(error);
}
Object.assign(file, info);
resolve(info);
});
});
const values = getFileData({ app: this.app, file, storage, request: { body: {} } } as any);
return await collectionRepository.create({ values, transaction });
}
async loadStorages(options?: { transaction: any }) {
const repository = this.db.getRepository('storages');
const storages = await repository.find({