mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-07-02 11:12:20 +08:00
feat: create file record via path (#5088)
This commit is contained in:
parent
fc86ec5fa5
commit
f3d15d5e8e
@ -11,6 +11,7 @@ import { promises as fs } from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { getApp } from '.';
|
import { getApp } from '.';
|
||||||
import { FILE_FIELD_NAME, FILE_SIZE_LIMIT_DEFAULT, STORAGE_TYPE_LOCAL } from '../../constants';
|
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;
|
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('create / upload', () => {
|
||||||
describe('default storage', () => {
|
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 () => {
|
it('upload file should be ok', async () => {
|
||||||
const { body } = await agent.resource('attachments').create({
|
const { body } = await agent.resource('attachments').create({
|
||||||
[FILE_FIELD_NAME]: path.resolve(__dirname, './files/text.txt'),
|
[FILE_FIELD_NAME]: path.resolve(__dirname, './files/text.txt'),
|
||||||
|
@ -11,15 +11,15 @@ import { Context, Next } from '@nocobase/actions';
|
|||||||
import { koaMulter as multer } from '@nocobase/utils';
|
import { koaMulter as multer } from '@nocobase/utils';
|
||||||
import Path from 'path';
|
import Path from 'path';
|
||||||
|
|
||||||
|
import Plugin from '..';
|
||||||
import {
|
import {
|
||||||
|
FILE_FIELD_NAME,
|
||||||
FILE_SIZE_LIMIT_DEFAULT,
|
FILE_SIZE_LIMIT_DEFAULT,
|
||||||
FILE_SIZE_LIMIT_MAX,
|
FILE_SIZE_LIMIT_MAX,
|
||||||
FILE_FIELD_NAME,
|
|
||||||
LIMIT_FILES,
|
|
||||||
FILE_SIZE_LIMIT_MIN,
|
FILE_SIZE_LIMIT_MIN,
|
||||||
|
LIMIT_FILES,
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
import * as Rules from '../rules';
|
import * as Rules from '../rules';
|
||||||
import Plugin from '..';
|
|
||||||
|
|
||||||
// TODO(optimize): 需要优化错误处理,计算失败后需要抛出对应错误,以便程序处理
|
// TODO(optimize): 需要优化错误处理,计算失败后需要抛出对应错误,以便程序处理
|
||||||
function getFileFilter(storage) {
|
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;
|
const { [FILE_FIELD_NAME]: file, storage } = ctx;
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return ctx.throw(400, 'file validation failed');
|
return ctx.throw(400, 'file validation failed');
|
||||||
|
@ -7,29 +7,77 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from 'path';
|
|
||||||
|
|
||||||
import { Plugin } from '@nocobase/server';
|
import { Plugin } from '@nocobase/server';
|
||||||
import { Registry } from '@nocobase/utils';
|
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 { FileModel } from './FileModel';
|
||||||
import initActions from './actions';
|
import initActions from './actions';
|
||||||
|
import { getFileData } from './actions/attachments';
|
||||||
|
import { AttachmentInterface } from './interfaces/attachment-interface';
|
||||||
import { IStorage, StorageModel } from './storages';
|
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 StorageTypeAliOss from './storages/ali-oss';
|
||||||
|
import StorageTypeLocal from './storages/local';
|
||||||
import StorageTypeS3 from './storages/s3';
|
import StorageTypeS3 from './storages/s3';
|
||||||
import StorageTypeTxCos from './storages/tx-cos';
|
import StorageTypeTxCos from './storages/tx-cos';
|
||||||
import { AttachmentInterface } from './interfaces/attachment-interface';
|
|
||||||
|
|
||||||
export type * from './storages';
|
export type * from './storages';
|
||||||
|
|
||||||
const DEFAULT_STORAGE_TYPE = STORAGE_TYPE_LOCAL;
|
const DEFAULT_STORAGE_TYPE = STORAGE_TYPE_LOCAL;
|
||||||
|
|
||||||
|
export type FileRecordOptions = {
|
||||||
|
collection: string;
|
||||||
|
filePath: string;
|
||||||
|
} & Transactionable;
|
||||||
|
|
||||||
export default class PluginFileManagerServer extends Plugin {
|
export default class PluginFileManagerServer extends Plugin {
|
||||||
storageTypes = new Registry<IStorage>();
|
storageTypes = new Registry<IStorage>();
|
||||||
storagesCache = new Map<number, StorageModel>();
|
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 }) {
|
async loadStorages(options?: { transaction: any }) {
|
||||||
const repository = this.db.getRepository('storages');
|
const repository = this.db.getRepository('storages');
|
||||||
const storages = await repository.find({
|
const storages = await repository.find({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user