doomistorage 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getFileUploader = void 0;
7
+ const multer_1 = __importDefault(require("multer"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const localfile_1 = require("./localfile");
11
+ class LocalUploader {
12
+ constructor(config) {
13
+ this.uploadconfig = config !== null && config !== void 0 ? config : {};
14
+ this.fileUtilily = new localfile_1.LocalFile();
15
+ }
16
+ get diskStorage() {
17
+ return multer_1.default.diskStorage({
18
+ //设置上传后文件路径,uploads文件夹会自动创建。
19
+ destination: (req, _file, cb) => {
20
+ ///获取上传的文件的类型,以便将文件保存到指定的目录
21
+ let filetype = req.query.filetype;
22
+ ////不允许没有配置的文件
23
+ if (!filetype) {
24
+ req.fileerror = { successed: false, errmsg: "missing filetype for upload" };
25
+ return cb("missing filetype for upload", null);
26
+ }
27
+ let saveOption = this.uploadconfig.mapping[filetype];
28
+ if (!saveOption) {
29
+ req.fileerror = { successed: false, errmsg: "filetype not configurated in upload setting" };
30
+ return cb("filetype not configurated in upload setting", null);
31
+ }
32
+ req.fileconfig = saveOption;
33
+ /**
34
+ * 将url中的参数拼装成对象,以匹配文件存储的设置
35
+ */
36
+ let fileparam = Object.assign({}, req.user, req.query);
37
+ let destinationFolder = this.fileUtilily.getSaveFolder(saveOption, fileparam);
38
+ ///文件上传后的短路径,相对路径,
39
+ req.shortpath = destinationFolder;
40
+ ////如果目录不存在,则创建目录
41
+ destinationFolder = path_1.default.join(this.uploadconfig.dest, destinationFolder);
42
+ if (!fs_1.default.existsSync(destinationFolder))
43
+ this.fileUtilily.mkdirsSync(path_1.default.resolve(destinationFolder));
44
+ cb(null, destinationFolder);
45
+ },
46
+ //给上传文件重命名,获取添加后缀名
47
+ filename: (req, file, cb) => {
48
+ let saveOption = req.fileconfig; //this.config.mapping[filetype];
49
+ if (!saveOption) {
50
+ req.fileerror = { successed: false, errmsg: "filetype not configurated in upload setting" };
51
+ cb("filetype not configurated in upload setting", null);
52
+ }
53
+ let finalFile = this.fileUtilily.getSaveOnlyFileName(saveOption, file.originalname);
54
+ req.shortpath = path_1.default.join(req.shortpath, finalFile);
55
+ cb(null, finalFile);
56
+ //cb(null, file.fieldname + '-' + Date.now() + "." + fileFormat[fileFormat.length - 1]);
57
+ }
58
+ });
59
+ }
60
+ }
61
+ function getFileUploader(uploadConfig) {
62
+ let handler = new LocalUploader(uploadConfig);
63
+ return (0, multer_1.default)({ storage: handler.diskStorage });
64
+ }
65
+ exports.getFileUploader = getFileUploader;
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "doomistorage",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.js",
7
+ "scripts": {
8
+ "build": "tsc --outDir dist"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "devDependencies": {
14
+ "@types/multer": "^1.4.7",
15
+ "@types/node": "^18.15.3",
16
+ "@types/node-uuid": "^0.0.29",
17
+ "typescript": "^5.0.2"
18
+ },
19
+ "dependencies": {
20
+ "axios": "^1.3.4",
21
+ "cos-nodejs-sdk-v5": "^2.12.1",
22
+ "moment": "^2.29.4",
23
+ "multer": "^1.4.5-lts.1",
24
+ "node-uuid": "^1.4.8"
25
+ }
26
+ }
package/src/cosfile.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { FileConfig, FileResult } from "./declare";
2
+ import { FileBase } from "./file";
3
+ import COS from 'cos-nodejs-sdk-v5';
4
+
5
+ export class CosFile extends FileBase{
6
+
7
+ private bucket : string; ///存储桶名称
8
+ private cos: COS; ///腾讯云COS对象
9
+ private region : string; ///存储桶所在的地区
10
+ constructor(config:any) {
11
+ super();
12
+ this.cos = new COS({
13
+ SecretId: config.SecretId,
14
+ SecretKey: config.SecretKey
15
+ });
16
+ this.bucket = config.bucket;
17
+ this.region = config.region;
18
+ }
19
+ /**
20
+ *
21
+ * @param data
22
+ * @param fileName
23
+ * @param saveOption
24
+ * @param userInfo
25
+ */
26
+ saveString2File(data: string|Buffer, fileName: string, saveOption: FileConfig, userInfo: any = {}): Promise<FileResult> {
27
+ if (!data) data = '';
28
+ let destinationFileName = this.getSaveFileName(saveOption, fileName, userInfo);
29
+ const streamBuffer = data instanceof Buffer ? data : Buffer.from(data, "utf-8");
30
+ const fileParam = {
31
+ Bucket: this.bucket,
32
+ Region: this.region,
33
+ Key: destinationFileName,
34
+ Body: streamBuffer,
35
+ ContentLength: streamBuffer.byteLength
36
+ };
37
+ return new Promise((success) => {
38
+ this.cos.putObject(fileParam, (err:any)=> {
39
+ return success({ successed: err == null, filePath: destinationFileName});
40
+ });
41
+ });
42
+ }
43
+ /**
44
+ *
45
+ * @param file
46
+ * @param fileName
47
+ * @param saveOption
48
+ * @param userInfo
49
+ * @returns
50
+ */
51
+ async saveFileStream(file: any, fileName: any, saveOption: any, userInfo: any): Promise<FileResult> {
52
+ const destinationFileName = this.getSaveFileName(saveOption, fileName, userInfo);
53
+ return new Promise((resolve) => {
54
+ if (saveOption.reRead) {
55
+ let dataArr:any =[],//存储读取的结果集合
56
+ len = 0;
57
+ file.on('data', (chunk:any)=> {
58
+ dataArr.push(chunk);
59
+ len += chunk.length;
60
+ });
61
+ file.on('end', ()=> {
62
+ const fileParam = {
63
+ Bucket: this.bucket,
64
+ Region: this.region,
65
+ Key: destinationFileName,
66
+ Body: Buffer.concat(dataArr, len), //file,
67
+ ContentLength: len// file.byteCount || saveOption.contentLength
68
+ };
69
+ this.cos.putObject(fileParam, (err)=> {
70
+ return resolve({ successed: err == null, filePath: destinationFileName })
71
+
72
+ })
73
+ });
74
+ } else {
75
+ const fileParam = {
76
+ Bucket: this.bucket,
77
+ Region: this.region,
78
+ // ContentDisposition: "form-data; name=\"file\"; filename=\"" + encodeURIComponent(destinationFileName) + "\"",
79
+ Key: destinationFileName,
80
+ Body: file,
81
+ ContentLength: file.byteCount || file.length
82
+ };
83
+
84
+ this.cos.putObject(fileParam, function (err) {
85
+ return resolve({ successed: err == null, filePath: destinationFileName })
86
+ })
87
+ }
88
+ });
89
+ }
90
+
91
+ }
package/src/declare.ts ADDED
@@ -0,0 +1,16 @@
1
+ export interface FileConfig{
2
+ 'subFolder':string,
3
+ 'saveDir':string,
4
+ 'fileName':string,
5
+ 'reRead'?:boolean,
6
+ 'basefolder'?:string ////根路径,主要对local使用
7
+ }
8
+ /**
9
+ * 文件的API
10
+ */
11
+ export interface FileResult {
12
+ 'successed': boolean;
13
+ 'error'?: string;
14
+ 'errcode'?: number;
15
+ 'filePath'?:string
16
+ }
package/src/file.ts ADDED
@@ -0,0 +1,222 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import Moment from 'moment';
4
+ import { v4 as UUIDV4 } from 'node-uuid';
5
+ import { FileConfig, FileResult } from './declare';
6
+ export abstract class FileBase {
7
+ /**
8
+ * 根据DoomiSoft框架生成的唯一文件名,用户上传后的文件存储后名称,防止重复
9
+ * @param {*} saveOption
10
+ * @param {*} fileName
11
+ * @param {*} userInfo
12
+ */
13
+ getSaveFileName(saveOption: FileConfig, fileName:string, userInfo:any = {}):string {
14
+ let saveFolder = this.getSaveFolder(saveOption,userInfo)
15
+ console.log('getSaveFileName', fileName)
16
+ let _fileName = this.getSaveOnlyFileName(saveOption, fileName, userInfo);
17
+ return path.join(saveFolder, _fileName);
18
+ }
19
+ /**
20
+ * 获取文件需要保存的目录
21
+ * @param {*} saveOption
22
+ * @param {*} userInfo
23
+ */
24
+ getSaveFolder(saveOption: FileConfig,userInfo:any ={}):string {
25
+ let subFolder = '';
26
+ ///分目录存储
27
+ switch ((saveOption.subFolder || '').toLowerCase()) {
28
+ ///按日建造一个目录
29
+ case 'onebydate': subFolder = Moment().format('YYYYMMDD'); break;
30
+ ///按日建造多级目录
31
+ case 'mutilbydate': subFolder = Moment().year() + path.sep + Moment().month() + path.sep + Moment().day(); break;
32
+ ///用自己的id(当前登录的id)建造目录
33
+ case 'identity': subFolder = userInfo.id??UUIDV4(); break;
34
+ }
35
+ let saveFolder = path.join(saveOption.saveDir, subFolder+'');
36
+ if (userInfo && userInfo.subfolder) saveFolder = path.join(saveFolder, userInfo.subfolder);
37
+ ///如果包含当前调用用户的信息,则替换整个路径中的@@关键字
38
+ if (userInfo){
39
+ const matched=saveFolder.match(/@.*?@/g);
40
+ if (matched && matched.length>0)
41
+ matched.forEach(ele=>{
42
+ const matchValue = ele.substring(1,ele.length-1);
43
+ if (matchValue.indexOf(' ')>=0 || matchValue.indexOf(':')>=0 || matchValue.indexOf('=')>=0) return;
44
+ let keyName = ele.substring(1,ele.length-1);
45
+ const keyValue = userInfo[keyName] || ''
46
+ saveFolder = saveFolder.replace(ele,keyValue);
47
+ });
48
+ }
49
+ return saveFolder;
50
+ }
51
+ /**
52
+ * 获取上传的文件仅包含文件名
53
+ * @param {*} saveOption
54
+ * @param {*} fileName
55
+ * @param {*} userInfo
56
+ */
57
+ getSaveOnlyFileName(saveOption: FileConfig, fileName: string, userInfo: any = {}): string {
58
+ let _fileName='';
59
+ switch ((saveOption.fileName || 'keep').toLowerCase()) {
60
+ ///保持和原有文件一致的文件名
61
+ case "keep": _fileName = fileName; break;
62
+ ///随机命名,但后缀必须一致
63
+ case "random": _fileName = UUIDV4() + path.extname(fileName); break;
64
+ ///使用当前账号的id命名
65
+ case "identity": _fileName = (userInfo.id ?? UUIDV4()) + path.extname(fileName); break;
66
+ }
67
+ return _fileName;
68
+ }
69
+ /**
70
+ * 保存文件流
71
+ * @param fileName
72
+ * @param file
73
+ * @param saveOption
74
+ * @param userInfo
75
+ */
76
+ abstract saveFileStream(file: any, fileName: string, saveOption: FileConfig, userInfo: any): Promise<FileResult>;
77
+ /**
78
+ * 直接保存字符
79
+ * @param data
80
+ * @param fileName
81
+ * @param saveOption
82
+ * @param userInfo
83
+ */
84
+ abstract saveString2File(data: string | Buffer, fileName: string, saveOption: FileConfig, userInfo: any): Promise<FileResult>;
85
+
86
+
87
+ /**
88
+ * 创建多级目录
89
+ * @param dirpath
90
+ * @param mode
91
+ * @returns
92
+ */
93
+ mkdirsSync(dirpath:string, mode:any=null):boolean {
94
+ if (!fs.existsSync(dirpath)) {
95
+ let pathtmp = '', splitPath = dirpath.split(path.sep);
96
+ for (const dirname of splitPath){
97
+ if (dirname.length==0) {
98
+ pathtmp=path.sep;
99
+ continue;
100
+ }
101
+ if (pathtmp)
102
+ pathtmp = path.join(pathtmp, dirname);
103
+ else
104
+ pathtmp = dirname;
105
+ if (!fs.existsSync(pathtmp)) {
106
+ fs.mkdirSync(pathtmp, mode)
107
+ }
108
+ }
109
+ }
110
+ return true;
111
+ }
112
+
113
+ /**
114
+ * 删除文件夹
115
+ * @param {*} path
116
+ */
117
+ // deleteFolder(path:string) {
118
+ // if( fs.existsSync(path) ) {
119
+ // const files = fs.readdirSync(path);
120
+ // files.forEach(function(file,index){
121
+ // var curPath = path + "/" + file;
122
+ // if(fs.statSync(curPath).isDirectory()) { // recurse
123
+ // fileUtility.deleteFolder(curPath);
124
+ // } else { // delete file
125
+ // fs.unlinkSync(curPath);
126
+ // }
127
+ // });
128
+ // fs.rmdirSync(path);
129
+ // }
130
+ // };
131
+
132
+
133
+
134
+ /**
135
+ * 从远程下载文件并保存到腾讯云本地
136
+ * @param {*} saveDestination
137
+ * @param {*} fileUrl
138
+ * @param {*} savesetting
139
+ * @param {*} keyid
140
+ */
141
+ // static downloadFile4upload(saveDestination,fileUrl,savesetting,userinfo,allowWebPFormat = false) {
142
+ // let originUrl = fileUrl;
143
+ // ///是否允许WebP格式的文件被下载
144
+ // if(!allowWebPFormat &&fileUrl.indexOf('&tp=webp')>=0) fileUrl = fileUrl.replace('&tp=webp','');
145
+ // let fileOption = urlUtility.parse(fileUrl);
146
+ // let filename = path.basename(fileOption.path);
147
+ // if(filename.indexOf('?')>=0) filename=filename.substr(0,filename.indexOf('?'));
148
+ // if (path.extname(filename)==''){
149
+ // let param = querystring.parse(fileOption.query)
150
+ // if (param.wx_fmt)
151
+ // filename=filename+"."+param.wx_fmt;
152
+ // else
153
+ // filename=filename+".jpeg";
154
+ // }
155
+ // const http =fileOption.protocol==='https:'? require('https'):require('http'); //require("../rpc/rpcUtility");
156
+ // return new Promise((resolve, reject) => {
157
+ // try{
158
+ // http.get(fileUrl,(res) => {
159
+ // var dataArr = [], len = 0;
160
+ // res.on('data',(chunk)=>{
161
+ // dataArr.push(chunk);
162
+ // len += chunk.length;
163
+ // })
164
+ // res.on("end", function (err) {
165
+ // File.uploadFile(saveDestination, filename, Buffer.concat(dataArr, len),savesetting,userinfo, function (file, result) {
166
+ // result.source = originUrl;
167
+ // if (result.successed) result.state = "SUCCESS";
168
+ // resolve(result);
169
+ // });
170
+ // });
171
+ // })
172
+ // }
173
+ // catch(err){
174
+ // console.log('download file error :',err);
175
+ // reject({successed:false,error:err});
176
+ // }
177
+ // })
178
+ // }
179
+ // static save2localForRemoteImage(sourceUrlArr, saveOption, userInfo, callback) {
180
+ // File.promiseAll(sourceUrlArr, saveOption, userInfo).then(function (values) {
181
+ // callback(null, values);
182
+ // }).catch(function (err) {
183
+ // callback(new Error("抓取报错"));
184
+ // });
185
+ // }
186
+ // static promiseAll(urlArr, saveOption, userInfo) {
187
+ // let promiseArr = [];
188
+ // for (let url of urlArr) {
189
+ // try {
190
+ // promiseArr.push(File.downloadFile4upload('tencentcos',url,saveOption,userInfo))
191
+ // } catch (error) {
192
+ // console.log('error happened',error)
193
+ // }
194
+ // }
195
+ // return Promise.all(promiseArr);
196
+ // }
197
+ /**
198
+ * 上传本地目录至远程服务器
199
+ * @param {*} localFile
200
+ * @param {*} saveDestination
201
+ * @param {*} savesetting
202
+ * @param {*} keyid
203
+ */
204
+ // uploadlocalFolder(path,saveDestination,savesetting,keyParam){
205
+ // if( fs.existsSync(path) ) {
206
+ // const files = fs.readdirSync(path);
207
+ // files.forEach(function(file,index){
208
+ // var curPath = path + "/" + file;
209
+ // if(fs.statSync(curPath).isDirectory()) { // recurse
210
+ // this.uploadlocalFolder(curPath);
211
+ // } else { // delete file
212
+ // //fs.unlinkSync(curPath);
213
+ // let data = fs.readFileSync(curPath);
214
+ // File.uploadFile(saveDestination, path.basename(curPath), data,savesetting,keyParam,function (file, result) {
215
+ // resolve(result);
216
+ // });
217
+ // }
218
+ // });
219
+ // fs.rmdirSync(path);
220
+ // }
221
+ // }
222
+ }
@@ -0,0 +1,123 @@
1
+ import { CosFile } from "./cosfile";
2
+ import { FileConfig, FileResult } from "./declare";
3
+ import { FileBase } from "./file";
4
+ import { LocalFile } from "./localfile";
5
+ import axios from 'axios';
6
+ import path from 'path';
7
+
8
+ /**
9
+ * 文件处理器类型
10
+ */
11
+ export const FileProviderEnum = {
12
+ LOCAL: 'local',
13
+ TENCENTCOS: 'tencentcos',
14
+ } as const;
15
+ export type FileProviderEnum = typeof FileProviderEnum[keyof typeof FileProviderEnum];
16
+ /**
17
+ * 获取到文件处理的对象
18
+ * @param destination
19
+ * @returns
20
+ */
21
+ export function FileHelper(provider: FileProviderEnum, apiOption?:any): FileUtility {
22
+ let filehandler:FileBase
23
+ switch (provider) {
24
+ case FileProviderEnum.LOCAL: filehandler = new LocalFile(); break;
25
+ case FileProviderEnum.TENCENTCOS: filehandler = new CosFile(apiOption); break;
26
+ default: filehandler = new LocalFile();
27
+ }
28
+ return new FileUtility(filehandler)
29
+ }
30
+
31
+
32
+ /**
33
+ * 文件帮助工具
34
+ */
35
+ class FileUtility {
36
+ private fileHandler: FileBase;
37
+ constructor(handler: FileBase) {
38
+ this.fileHandler = handler;
39
+ }
40
+ /**
41
+ * 读取文件并保存至目标路径
42
+ * @param file 文件对象
43
+ * @param provider : 文件处理的实例名
44
+ * @param fileName :最终需要保存的文件名称
45
+ * @param saveOption :
46
+ * @param userInfo
47
+ * @returns
48
+ */
49
+ async SaveFileStream(
50
+ fileName: string,
51
+ file: any,
52
+ saveOption: FileConfig,
53
+ userInfo: any = {}): Promise<FileResult> {
54
+ return await this.fileHandler.saveFileStream(file, fileName, saveOption, userInfo);
55
+ }
56
+
57
+ /**
58
+ * 保存字符流到对应的存储设备
59
+ * @param bufferData
60
+ * @param fileName
61
+ * @param saveOption
62
+ * @param userInfo
63
+ * @returns
64
+ */
65
+ async SaveString2File(
66
+ bufferData: any,
67
+ fileName: string,
68
+ saveOption: FileConfig,
69
+ userInfo: any = {}): Promise<FileResult> {
70
+ return await this.fileHandler.saveString2File(bufferData,fileName, saveOption, userInfo);
71
+ }
72
+
73
+ /**
74
+ * 下载文件并且保存
75
+ * @param saveDestination
76
+ * @param fileUrl
77
+ * @param savesetting
78
+ * @param userinfo
79
+ * @param allowWebPFormat
80
+ * @returns
81
+ */
82
+ async DownloadFile(fileUrl: string, savefileName:string, savesetting: FileConfig, userInfo: any = {}, allowWebPFormat = false): Promise<FileResult> {
83
+ if (!allowWebPFormat && fileUrl.indexOf('&tp=webp') >= 0) fileUrl = fileUrl.replace('&tp=webp', '');
84
+ let fileName = savefileName ?savefileName:path.basename(fileUrl);
85
+ try{
86
+ const downloadResult = await axios.get(fileUrl, { responseType: 'arraybuffer' });
87
+ if (downloadResult.data){
88
+ return this.SaveString2File(downloadResult.data, fileName, savesetting,userInfo);
89
+ }
90
+ return { successed: false, error: '下载文件中没有任何内容' }
91
+ }catch(error:any){
92
+ return { successed: false, error: error}
93
+ }
94
+ }
95
+ /**
96
+ * 预测生成文件保存后的文件路径
97
+ * @param saveOption
98
+ * @param fileName
99
+ * @param userInfo
100
+ */
101
+ getSaveFileName(saveOption: FileConfig, fileName: string, userInfo: any = {}):string{
102
+ return this.fileHandler.getSaveFileName(saveOption, fileName, userInfo);
103
+ }
104
+
105
+ /**
106
+ * 预测生成文件保存后的文件路径
107
+ * @param saveOption
108
+ * @param fileName
109
+ * @param userInfo
110
+ */
111
+ getSaveFolder(saveOption: FileConfig, userInfo: any = {}): string {
112
+ return this.fileHandler.getSaveFolder(saveOption, userInfo);
113
+ }
114
+ /**
115
+ * 预测生成文件保存后的文件名称
116
+ * @param saveOption
117
+ * @param fileName
118
+ * @param userInfo
119
+ */
120
+ getSaveOnlyFileName(saveOption: FileConfig, fileName: string, userInfo: any = {}): string {
121
+ return this.fileHandler.getSaveOnlyFileName(saveOption,fileName,userInfo);
122
+ }
123
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+
2
+ export * from './filehelper'
3
+ export * from './uploader'
4
+
5
+
@@ -0,0 +1,45 @@
1
+ import { FileBase } from "./file";
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { FileConfig, FileResult } from "./declare";
5
+ export class LocalFile extends FileBase{
6
+ /**
7
+ * 直接保存字符串
8
+ * @param data
9
+ * @param fileName
10
+ * @param saveOption
11
+ * @param userInfo
12
+ */
13
+ async saveString2File(data: string|Buffer, fileName: string, saveOption: FileConfig, userInfo: any = {}): Promise<FileResult> {
14
+ let baseFileName = this.getSaveFileName(saveOption, fileName, userInfo);
15
+ if (!baseFileName) return { successed: false };
16
+ let fullFileName = path.resolve(saveOption.basefolder || '', baseFileName);
17
+ let _saveDir = path.dirname(fullFileName);
18
+ ///创建本地文件夹
19
+ if (!this.mkdirsSync(_saveDir)) return { successed: false };
20
+ return new Promise(resolve=>{
21
+ fs.writeFile(fullFileName, data,(error)=>{
22
+ return resolve({ successed: error == null, filePath: fullFileName })
23
+ })
24
+ });
25
+ }
26
+ /**
27
+ * 读取文件对象并保存至本地存储
28
+ * @param file
29
+ * @param fileName
30
+ * @param saveOption
31
+ * @param userInfo
32
+ * @returns
33
+ */
34
+ async saveFileStream(file: any, fileName: any, saveOption: FileConfig, userInfo: any): Promise<FileResult> {
35
+ let baseFileName = this.getSaveFileName(saveOption, fileName, userInfo);
36
+ if (!baseFileName) return { successed: false };
37
+ let fullFileName = path.resolve(saveOption.basefolder||'', baseFileName);
38
+ let _saveDir = path.dirname(fullFileName);
39
+ ///创建本地文件夹
40
+ if (!this.mkdirsSync(_saveDir)) return { successed: false };
41
+ file.pipe(fs.createWriteStream(fullFileName));
42
+ return { successed: true, filePath: fullFileName };
43
+ }
44
+
45
+ }
@@ -0,0 +1,62 @@
1
+ import multer from 'multer';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import { LocalFile } from './localfile';
5
+
6
+ class LocalUploader{
7
+ private uploadconfig:any;
8
+ private fileUtilily: LocalFile;
9
+ constructor(config:any){
10
+ this.uploadconfig = config??{}
11
+ this.fileUtilily = new LocalFile();
12
+ }
13
+
14
+ get diskStorage():any{
15
+ return multer.diskStorage({
16
+ //设置上传后文件路径,uploads文件夹会自动创建。
17
+ destination: (req: any, _file: any, cb: any) => {
18
+ ///获取上传的文件的类型,以便将文件保存到指定的目录
19
+ let filetype = req.query.filetype;
20
+ ////不允许没有配置的文件
21
+ if (!filetype) {
22
+ req.fileerror = { successed: false, errmsg: "missing filetype for upload" };
23
+ return cb("missing filetype for upload", null);
24
+ }
25
+ let saveOption = this.uploadconfig.mapping[filetype];
26
+ if (!saveOption) {
27
+ req.fileerror = { successed: false, errmsg: "filetype not configurated in upload setting" };
28
+ return cb("filetype not configurated in upload setting", null);
29
+ }
30
+ req.fileconfig = saveOption;
31
+ /**
32
+ * 将url中的参数拼装成对象,以匹配文件存储的设置
33
+ */
34
+ let fileparam = Object.assign({}, req.user, req.query);
35
+ let destinationFolder = this.fileUtilily.getSaveFolder(saveOption, fileparam);
36
+ ///文件上传后的短路径,相对路径,
37
+ req.shortpath = destinationFolder;
38
+ ////如果目录不存在,则创建目录
39
+ destinationFolder = path.join(this.uploadconfig.dest, destinationFolder);
40
+ if (!fs.existsSync(destinationFolder)) this.fileUtilily.mkdirsSync(path.resolve(destinationFolder))
41
+ cb(null, destinationFolder)
42
+ },
43
+ //给上传文件重命名,获取添加后缀名
44
+ filename: (req: any, file: any, cb: any) => {
45
+ let saveOption = req.fileconfig; //this.config.mapping[filetype];
46
+ if (!saveOption) {
47
+ req.fileerror = { successed: false, errmsg: "filetype not configurated in upload setting" };
48
+ cb("filetype not configurated in upload setting", null);
49
+ }
50
+ let finalFile = this.fileUtilily.getSaveOnlyFileName(saveOption, file.originalname);
51
+ req.shortpath = path.join(req.shortpath, finalFile);
52
+ cb(null, finalFile);
53
+ //cb(null, file.fieldname + '-' + Date.now() + "." + fileFormat[fileFormat.length - 1]);
54
+ }
55
+ });
56
+ }
57
+ }
58
+
59
+ export function getFileUploader(uploadConfig:any){
60
+ let handler = new LocalUploader(uploadConfig);
61
+ return multer({ storage: handler.diskStorage });
62
+ }