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.
- package/dist/cosfile.d.ts +26 -0
- package/dist/cosfile.js +102 -0
- package/dist/declare.d.ts +16 -0
- package/dist/declare.js +2 -0
- package/dist/file.d.ts +47 -0
- package/dist/file.js +116 -0
- package/dist/filehelper.d.ts +74 -0
- package/dist/filehelper.js +133 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/localfile.d.ts +22 -0
- package/dist/localfile.js +67 -0
- package/dist/uploader.d.ts +2 -0
- package/dist/uploader.js +65 -0
- package/package.json +26 -0
- package/src/cosfile.ts +91 -0
- package/src/declare.ts +16 -0
- package/src/file.ts +222 -0
- package/src/filehelper.ts +123 -0
- package/src/index.ts +5 -0
- package/src/localfile.ts +45 -0
- package/src/uploader.ts +62 -0
- package/tsconfig.json +31 -0
package/dist/uploader.js
ADDED
|
@@ -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
package/src/localfile.ts
ADDED
|
@@ -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
|
+
}
|
package/src/uploader.ts
ADDED
|
@@ -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
|
+
}
|