@kevisual/oss 0.0.16 → 0.0.18
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/index.d.ts +266 -152
- package/dist/index.js +36542 -112
- package/dist/services.d.ts +330 -0
- package/dist/services.js +36766 -0
- package/package.json +9 -14
- package/src/index.ts +2 -243
- package/src/s3/core.ts +82 -24
- package/src/s3/type.ts +7 -0
- package/src/services/config.ts +2 -2
- package/src/services/index.ts +1 -1
- package/src/test/common.ts +1 -1
- package/src/util/download.ts +69 -69
- package/dist/services/config.d.ts +0 -190
- package/dist/services/config.js +0 -358
- package/dist/services/index.d.ts +0 -235
- package/dist/services/index.js +0 -421
- package/src/core/copy-object.ts +0 -26
- package/src/core/type.ts +0 -87
package/dist/services/index.d.ts
DELETED
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
-
|
|
3
|
-
import { Client, ItemBucketMetadata } from 'minio';
|
|
4
|
-
import { ServerResponse } from 'node:http';
|
|
5
|
-
|
|
6
|
-
export type UploadedObjectInfo = {
|
|
7
|
-
etag: string;
|
|
8
|
-
lastModified?: Date;
|
|
9
|
-
size?: number;
|
|
10
|
-
versionId: string;
|
|
11
|
-
metadata?: ItemBucketMetadata;
|
|
12
|
-
};
|
|
13
|
-
export type StatObjectResult = {
|
|
14
|
-
size: number;
|
|
15
|
-
etag: string;
|
|
16
|
-
lastModified: Date;
|
|
17
|
-
metaData: ItemBucketMetadata;
|
|
18
|
-
versionId?: string | null;
|
|
19
|
-
};
|
|
20
|
-
export type ListFileObject = {
|
|
21
|
-
name: string;
|
|
22
|
-
size: number;
|
|
23
|
-
lastModified: Date;
|
|
24
|
-
etag: string;
|
|
25
|
-
};
|
|
26
|
-
export type ListDirectoryObject = {
|
|
27
|
-
prefix: string;
|
|
28
|
-
size: number;
|
|
29
|
-
};
|
|
30
|
-
export type ListObjectResult = ListFileObject | ListDirectoryObject;
|
|
31
|
-
export interface OssBaseOperation {
|
|
32
|
-
prefix: string;
|
|
33
|
-
setPrefix(prefix: string): void;
|
|
34
|
-
/**
|
|
35
|
-
* 获取对象
|
|
36
|
-
* @param objectName 对象名
|
|
37
|
-
*/
|
|
38
|
-
getObject(objectName: string): Promise<any>;
|
|
39
|
-
/**
|
|
40
|
-
* 上传对象
|
|
41
|
-
* @param objectName 对象名
|
|
42
|
-
* @param data 数据
|
|
43
|
-
*/
|
|
44
|
-
putObject(objectName: string, data: Buffer | string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo>;
|
|
45
|
-
/**
|
|
46
|
-
* 上传文件
|
|
47
|
-
* @param objectName 对象名
|
|
48
|
-
* @param filePath 文件路径
|
|
49
|
-
*/
|
|
50
|
-
fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo>;
|
|
51
|
-
/**
|
|
52
|
-
* 获取对象信息
|
|
53
|
-
* @param objectName 对象名
|
|
54
|
-
*/
|
|
55
|
-
statObject(objectName: string): Promise<StatObjectResult>;
|
|
56
|
-
/**
|
|
57
|
-
* 删除对象
|
|
58
|
-
* @param objectName 对象名
|
|
59
|
-
*/
|
|
60
|
-
deleteObject(objectName: string): Promise<any>;
|
|
61
|
-
/**
|
|
62
|
-
* 列出对象
|
|
63
|
-
* @param objectName 对象名
|
|
64
|
-
* @param opts 选项
|
|
65
|
-
* @param opts.recursive 是否递归
|
|
66
|
-
* @param opts.startAfter 开始位置
|
|
67
|
-
*/
|
|
68
|
-
listObjects(objectName: string, opts?: {
|
|
69
|
-
/**
|
|
70
|
-
* 是否递归
|
|
71
|
-
*/
|
|
72
|
-
recursive?: boolean;
|
|
73
|
-
/**
|
|
74
|
-
* 开始位置
|
|
75
|
-
*/
|
|
76
|
-
startAfter?: string;
|
|
77
|
-
}): Promise<ListObjectResult[]>;
|
|
78
|
-
/**
|
|
79
|
-
* 复制对象
|
|
80
|
-
* @param sourceObject 源对象
|
|
81
|
-
* @param targetObject 目标对象
|
|
82
|
-
*/
|
|
83
|
-
copyObject: Client["copyObject"];
|
|
84
|
-
}
|
|
85
|
-
export interface OssService extends OssBaseOperation {
|
|
86
|
-
owner: string;
|
|
87
|
-
}
|
|
88
|
-
export type OssBaseOptions<T = {
|
|
89
|
-
[key: string]: any;
|
|
90
|
-
}> = {
|
|
91
|
-
/**
|
|
92
|
-
* 已经初始化好的minio client
|
|
93
|
-
*/
|
|
94
|
-
client: Client;
|
|
95
|
-
/**
|
|
96
|
-
* 桶名
|
|
97
|
-
*/
|
|
98
|
-
bucketName: string;
|
|
99
|
-
/**
|
|
100
|
-
* 前缀
|
|
101
|
-
*/
|
|
102
|
-
prefix?: string;
|
|
103
|
-
} & T;
|
|
104
|
-
export declare class OssBase implements OssBaseOperation {
|
|
105
|
-
client?: Client;
|
|
106
|
-
bucketName: string;
|
|
107
|
-
prefix: string;
|
|
108
|
-
/**
|
|
109
|
-
* 计算字符串或者对象的的md5值
|
|
110
|
-
*/
|
|
111
|
-
hash: (str: string | Buffer | Object) => string;
|
|
112
|
-
constructor(opts: OssBaseOptions);
|
|
113
|
-
setPrefix(prefix: string): void;
|
|
114
|
-
getObject(objectName: string): Promise<import("node:stream").Readable>;
|
|
115
|
-
getJson(objectName: string): Promise<Record<string, any>>;
|
|
116
|
-
/**
|
|
117
|
-
* 上传文件, 当是流的时候,中断之后的etag会变,所以传递的时候不要嵌套async await,例如 busboy 监听文件流内部的时候,不要用check
|
|
118
|
-
* @param objectName
|
|
119
|
-
* @param data
|
|
120
|
-
* @param metaData
|
|
121
|
-
* @param options 如果文件本身存在,则复制原有的meta的内容
|
|
122
|
-
* @returns
|
|
123
|
-
*/
|
|
124
|
-
putObject(objectName: string, data: Buffer | string | Object, metaData?: ItemBucketMetadata, opts?: {
|
|
125
|
-
check?: boolean;
|
|
126
|
-
isStream?: boolean;
|
|
127
|
-
size?: number;
|
|
128
|
-
}): Promise<import("node_modules/minio/dist/main/internal/type.js").UploadedObjectInfo>;
|
|
129
|
-
deleteObject(objectName: string): Promise<void>;
|
|
130
|
-
listObjects<IS_FILE = false>(objectName: string, opts?: {
|
|
131
|
-
recursive?: boolean;
|
|
132
|
-
startAfter?: string;
|
|
133
|
-
}): Promise<IS_FILE extends true ? ListFileObject[] : ListObjectResult[]>;
|
|
134
|
-
fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata): Promise<any>;
|
|
135
|
-
/**
|
|
136
|
-
* 获取完整的对象名称
|
|
137
|
-
* @param objectName
|
|
138
|
-
* @returns
|
|
139
|
-
*/
|
|
140
|
-
getObjectName(objectName: string): Promise<string>;
|
|
141
|
-
statObject(objectName: string, checkFile?: boolean): Promise<import("minio").BucketItemStat>;
|
|
142
|
-
/**
|
|
143
|
-
* 检查文件hash是否一致
|
|
144
|
-
* @param objectName
|
|
145
|
-
* @param hash
|
|
146
|
-
* @returns
|
|
147
|
-
*/
|
|
148
|
-
checkObjectHash(objectName: string, hash: string, meta?: ItemBucketMetadata): Promise<{
|
|
149
|
-
success: boolean;
|
|
150
|
-
metaData: ItemBucketMetadata | null;
|
|
151
|
-
obj: any;
|
|
152
|
-
equalMeta?: boolean;
|
|
153
|
-
}>;
|
|
154
|
-
getMetadata(pathname: string, meta?: ItemBucketMetadata): ItemBucketMetadata;
|
|
155
|
-
copyObject(sourceObject: any, targetObject: any): Promise<import("node_modules/minio/dist/main/internal/type.js").CopyObjectResult>;
|
|
156
|
-
replaceObject(objectName: string, meta: {
|
|
157
|
-
[key: string]: string;
|
|
158
|
-
}): Promise<import("node_modules/minio/dist/main/internal/type.js").CopyObjectResult>;
|
|
159
|
-
static create<T extends OssBase, U>(this: new (opts: OssBaseOptions<U>) => T, opts: OssBaseOptions<U>): T;
|
|
160
|
-
static fromBase<T extends OssBase, U>(this: new (opts: OssBaseOptions<U>) => T, createOpts: {
|
|
161
|
-
oss: OssBase;
|
|
162
|
-
opts: Partial<OssBaseOptions<U>>;
|
|
163
|
-
}): T;
|
|
164
|
-
}
|
|
165
|
-
export declare class ConfigOssService extends OssBase implements OssService {
|
|
166
|
-
owner: string;
|
|
167
|
-
constructor(opts: OssBaseOptions<{
|
|
168
|
-
owner: string;
|
|
169
|
-
}>);
|
|
170
|
-
listAllFile(): Promise<ListFileObject[]>;
|
|
171
|
-
listAll(): Promise<ListObjectResult[]>;
|
|
172
|
-
configMap: Map<string, any>;
|
|
173
|
-
keys: string[];
|
|
174
|
-
getAllConfigJson(): Promise<ListFileObject[]>;
|
|
175
|
-
isEndWithJson(string: string): boolean;
|
|
176
|
-
putJsonObject(key: string, data: any): Promise<import("node_modules/minio/dist/main/internal/type.js").UploadedObjectInfo>;
|
|
177
|
-
getObjectList(objectNameList: string[]): Promise<Map<string, Record<string, any>>>;
|
|
178
|
-
getList(): Promise<{
|
|
179
|
-
list: {
|
|
180
|
-
key: string;
|
|
181
|
-
name: string;
|
|
182
|
-
size: number;
|
|
183
|
-
lastModified: Date;
|
|
184
|
-
etag: string;
|
|
185
|
-
}[];
|
|
186
|
-
keys: string[];
|
|
187
|
-
keyEtagMap: Map<string, string>;
|
|
188
|
-
}>;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* 过滤 metaData 中的 key, 去除 password, accesskey, secretkey,
|
|
192
|
-
* 并返回过滤后的 metaData
|
|
193
|
-
* @param metaData
|
|
194
|
-
* @returns
|
|
195
|
-
*/
|
|
196
|
-
export declare const filterMetaDataKeys: (metaData: Record<string, string>, clearKeys?: string[]) => Record<string, string>;
|
|
197
|
-
export type SendObjectOptions = {
|
|
198
|
-
res: ServerResponse;
|
|
199
|
-
client: OssBase;
|
|
200
|
-
objectName: string;
|
|
201
|
-
isDownload?: boolean;
|
|
202
|
-
};
|
|
203
|
-
export declare const NotFoundFile: (res: ServerResponse, msg?: string, code?: number) => void;
|
|
204
|
-
export declare const sendObject: ({ res, objectName, client, isDownload }: SendObjectOptions) => Promise<void>;
|
|
205
|
-
export declare const downloadObject: ({ objectName, client, filePath }: Pick<SendObjectOptions, "objectName" | "client"> & {
|
|
206
|
-
filePath: string;
|
|
207
|
-
}) => Promise<import("node:stream").Readable>;
|
|
208
|
-
/**
|
|
209
|
-
* 计算字符串的md5值
|
|
210
|
-
* @param str
|
|
211
|
-
* @returns
|
|
212
|
-
*/
|
|
213
|
-
export declare const hash: (str: string | Buffer | Object) => string;
|
|
214
|
-
export declare const hashSringify: (str: Object) => string;
|
|
215
|
-
export declare const getContentType: (filePath: string) => any;
|
|
216
|
-
export declare const standardHeaderKeys: string[];
|
|
217
|
-
export type StandardHeaders = {
|
|
218
|
-
ContentType?: string;
|
|
219
|
-
CacheControl?: string;
|
|
220
|
-
ContentDisposition?: string;
|
|
221
|
-
ContentEncoding?: string;
|
|
222
|
-
ContentLanguage?: string;
|
|
223
|
-
Expires?: Date;
|
|
224
|
-
};
|
|
225
|
-
/**
|
|
226
|
-
* 从元数据中提取标准头部和自定义元数据
|
|
227
|
-
* @param metaData 原始元数据
|
|
228
|
-
* @returns 标准头部和自定义元数据
|
|
229
|
-
*/
|
|
230
|
-
export declare function extractStandardHeaders(metaData: Record<string, string>): {
|
|
231
|
-
standardHeaders: StandardHeaders;
|
|
232
|
-
customMetadata: Record<string, string>;
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
export {};
|
package/dist/services/index.js
DELETED
|
@@ -1,421 +0,0 @@
|
|
|
1
|
-
// src/util/hash.ts
|
|
2
|
-
import crypto from "node:crypto";
|
|
3
|
-
var hash = (str) => {
|
|
4
|
-
let hashStr;
|
|
5
|
-
if (str instanceof Buffer) {
|
|
6
|
-
hashStr = str;
|
|
7
|
-
} else if (str instanceof Object) {
|
|
8
|
-
hashStr = JSON.stringify(str, null, 2);
|
|
9
|
-
} else {
|
|
10
|
-
hashStr = str;
|
|
11
|
-
}
|
|
12
|
-
return crypto.createHash("md5").update(hashStr).digest("hex");
|
|
13
|
-
};
|
|
14
|
-
var hashSringify = (str) => {
|
|
15
|
-
return JSON.stringify(str, null, 2);
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// src/core/copy-object.ts
|
|
19
|
-
import { CopyDestinationOptions, CopySourceOptions } from "minio";
|
|
20
|
-
var copyObject = async ({ bucketName, newMetadata, objectName, client }) => {
|
|
21
|
-
const source = new CopySourceOptions({ Bucket: bucketName, Object: objectName });
|
|
22
|
-
const stat = await client.statObject(bucketName, objectName);
|
|
23
|
-
const sourceMetadata = stat.metaData;
|
|
24
|
-
const destination = new CopyDestinationOptions({
|
|
25
|
-
Bucket: bucketName,
|
|
26
|
-
Object: objectName,
|
|
27
|
-
UserMetadata: { ...sourceMetadata, ...newMetadata },
|
|
28
|
-
MetadataDirective: "REPLACE"
|
|
29
|
-
});
|
|
30
|
-
const copyResult = await client.copyObject(source, destination);
|
|
31
|
-
return copyResult;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
// src/util/get-content-type.ts
|
|
35
|
-
import path from "node:path";
|
|
36
|
-
var getContentType = (filePath) => {
|
|
37
|
-
const extname = path.extname(filePath);
|
|
38
|
-
const contentType = {
|
|
39
|
-
".html": "text/html; charset=utf-8",
|
|
40
|
-
".js": "text/javascript; charset=utf-8",
|
|
41
|
-
".css": "text/css; charset=utf-8",
|
|
42
|
-
".txt": "text/plain; charset=utf-8",
|
|
43
|
-
".json": "application/json; charset=utf-8",
|
|
44
|
-
".png": "image/png",
|
|
45
|
-
".jpg": "image/jpg",
|
|
46
|
-
".gif": "image/gif",
|
|
47
|
-
".svg": "image/svg+xml",
|
|
48
|
-
".wav": "audio/wav",
|
|
49
|
-
".mp4": "video/mp4",
|
|
50
|
-
".md": "text/markdown; charset=utf-8",
|
|
51
|
-
".ico": "image/x-icon",
|
|
52
|
-
".webp": "image/webp",
|
|
53
|
-
".webm": "video/webm",
|
|
54
|
-
".ogg": "audio/ogg",
|
|
55
|
-
".mp3": "audio/mpeg",
|
|
56
|
-
".m4a": "audio/mp4",
|
|
57
|
-
".m3u8": "application/vnd.apple.mpegurl",
|
|
58
|
-
".ts": "video/mp2t",
|
|
59
|
-
".pdf": "application/pdf",
|
|
60
|
-
".doc": "application/msword",
|
|
61
|
-
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
62
|
-
".ppt": "application/vnd.ms-powerpoint",
|
|
63
|
-
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
64
|
-
".xls": "application/vnd.ms-excel",
|
|
65
|
-
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
66
|
-
".csv": "text/csv; charset=utf-8",
|
|
67
|
-
".xml": "application/xml; charset=utf-8",
|
|
68
|
-
".rtf": "application/rtf",
|
|
69
|
-
".eot": "application/vnd.ms-fontobject",
|
|
70
|
-
".ttf": "font/ttf",
|
|
71
|
-
".woff": "font/woff",
|
|
72
|
-
".woff2": "font/woff2",
|
|
73
|
-
".otf": "font/otf",
|
|
74
|
-
".wasm": "application/wasm",
|
|
75
|
-
".pem": "application/x-pem-file",
|
|
76
|
-
".crt": "application/x-x509-ca-cert",
|
|
77
|
-
".yaml": "application/x-yaml; charset=utf-8",
|
|
78
|
-
".yml": "application/x-yaml; charset=utf-8",
|
|
79
|
-
".zip": "application/octet-stream"
|
|
80
|
-
};
|
|
81
|
-
return contentType[extname] || "application/octet-stream";
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
// node_modules/.pnpm/es-toolkit@1.43.0/node_modules/es-toolkit/dist/object/omit.mjs
|
|
85
|
-
function omit(obj, keys) {
|
|
86
|
-
const result = { ...obj };
|
|
87
|
-
for (let i = 0;i < keys.length; i++) {
|
|
88
|
-
const key = keys[i];
|
|
89
|
-
delete result[key];
|
|
90
|
-
}
|
|
91
|
-
return result;
|
|
92
|
-
}
|
|
93
|
-
// src/index.ts
|
|
94
|
-
class OssBase {
|
|
95
|
-
client;
|
|
96
|
-
bucketName;
|
|
97
|
-
prefix = "";
|
|
98
|
-
hash = hash;
|
|
99
|
-
constructor(opts) {
|
|
100
|
-
if (!opts.client) {
|
|
101
|
-
throw new Error("client is required");
|
|
102
|
-
}
|
|
103
|
-
this.bucketName = opts.bucketName;
|
|
104
|
-
this.client = opts.client;
|
|
105
|
-
this.prefix = opts?.prefix ?? "";
|
|
106
|
-
}
|
|
107
|
-
setPrefix(prefix) {
|
|
108
|
-
this.prefix = prefix;
|
|
109
|
-
}
|
|
110
|
-
async getObject(objectName) {
|
|
111
|
-
const bucketName = this.bucketName;
|
|
112
|
-
const obj = await this.client.getObject(bucketName, `${this.prefix}${objectName}`);
|
|
113
|
-
return obj;
|
|
114
|
-
}
|
|
115
|
-
async getJson(objectName) {
|
|
116
|
-
const obj = await this.getObject(objectName);
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
let data = "";
|
|
119
|
-
obj.on("data", (chunk) => {
|
|
120
|
-
data += chunk;
|
|
121
|
-
});
|
|
122
|
-
obj.on("end", () => {
|
|
123
|
-
try {
|
|
124
|
-
const jsonData = JSON.parse(data);
|
|
125
|
-
resolve(jsonData);
|
|
126
|
-
} catch (error) {
|
|
127
|
-
reject(new Error("Failed to parse JSON"));
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
obj.on("error", (err) => {
|
|
131
|
-
reject(err);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
async putObject(objectName, data, metaData = {}, opts) {
|
|
136
|
-
let putData;
|
|
137
|
-
let size = opts?.size;
|
|
138
|
-
const isStream = opts?.isStream;
|
|
139
|
-
if (!isStream) {
|
|
140
|
-
if (typeof data === "string") {
|
|
141
|
-
putData = data;
|
|
142
|
-
size = putData.length;
|
|
143
|
-
} else {
|
|
144
|
-
putData = JSON.stringify(data);
|
|
145
|
-
size = putData.length;
|
|
146
|
-
}
|
|
147
|
-
} else {
|
|
148
|
-
putData = data;
|
|
149
|
-
if (!size) {
|
|
150
|
-
throw new Error("Stream upload requires size parameter to avoid multipart upload and get standard MD5 ETag");
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (opts?.check) {
|
|
154
|
-
const obj2 = await this.statObject(objectName, true);
|
|
155
|
-
if (obj2) {
|
|
156
|
-
const omitMeda = ["size", "content-type", "cache-control", "app-source"];
|
|
157
|
-
const objMeta = JSON.parse(JSON.stringify(omit(obj2.metaData, omitMeda)));
|
|
158
|
-
metaData = {
|
|
159
|
-
...objMeta,
|
|
160
|
-
...metaData
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
const bucketName = this.bucketName;
|
|
165
|
-
const obj = await this.client.putObject(bucketName, `${this.prefix}${objectName}`, putData, size, metaData);
|
|
166
|
-
return obj;
|
|
167
|
-
}
|
|
168
|
-
async deleteObject(objectName) {
|
|
169
|
-
const bucketName = this.bucketName;
|
|
170
|
-
const obj = await this.client.removeObject(bucketName, `${this.prefix}${objectName}`);
|
|
171
|
-
return obj;
|
|
172
|
-
}
|
|
173
|
-
async listObjects(objectName, opts) {
|
|
174
|
-
const bucketName = this.bucketName;
|
|
175
|
-
const prefix = `${this.prefix}${objectName}`;
|
|
176
|
-
const res = await new Promise((resolve, reject) => {
|
|
177
|
-
let res2 = [];
|
|
178
|
-
let hasError = false;
|
|
179
|
-
this.client.listObjectsV2(bucketName, prefix, opts?.recursive ?? false, opts?.startAfter).on("data", (data) => {
|
|
180
|
-
res2.push(data);
|
|
181
|
-
}).on("error", (err) => {
|
|
182
|
-
console.error("minio error", prefix, err);
|
|
183
|
-
hasError = true;
|
|
184
|
-
}).on("end", () => {
|
|
185
|
-
if (hasError) {
|
|
186
|
-
reject();
|
|
187
|
-
return;
|
|
188
|
-
} else {
|
|
189
|
-
resolve(res2);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
return res;
|
|
194
|
-
}
|
|
195
|
-
async fPutObject(objectName, filePath, metaData) {
|
|
196
|
-
const bucketName = this.bucketName;
|
|
197
|
-
const obj = await this.client.fPutObject(bucketName, `${this.prefix}${objectName}`, filePath, metaData);
|
|
198
|
-
return obj;
|
|
199
|
-
}
|
|
200
|
-
async getObjectName(objectName) {
|
|
201
|
-
return `${this.prefix}${objectName}`;
|
|
202
|
-
}
|
|
203
|
-
async statObject(objectName, checkFile = true) {
|
|
204
|
-
const bucketName = this.bucketName;
|
|
205
|
-
try {
|
|
206
|
-
const obj = await this.client.statObject(bucketName, `${this.prefix}${objectName}`);
|
|
207
|
-
return obj;
|
|
208
|
-
} catch (e) {
|
|
209
|
-
if (e.code === "NotFound") {
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
throw e;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
async checkObjectHash(objectName, hash2, meta) {
|
|
216
|
-
const obj = await this.statObject(`${this.prefix}${objectName}`, true);
|
|
217
|
-
if (!obj) {
|
|
218
|
-
return { success: false, metaData: null, obj: null, equalMeta: false };
|
|
219
|
-
}
|
|
220
|
-
let metaData = {};
|
|
221
|
-
const omitMeda = ["content-type", "cache-control", "app-source"];
|
|
222
|
-
const objMeta = omit(obj.metaData, omitMeda);
|
|
223
|
-
metaData = {
|
|
224
|
-
...objMeta
|
|
225
|
-
};
|
|
226
|
-
let equalMeta = false;
|
|
227
|
-
if (meta) {
|
|
228
|
-
equalMeta = JSON.stringify(metaData) === JSON.stringify(meta);
|
|
229
|
-
}
|
|
230
|
-
return { success: obj.etag === hash2, metaData, obj, equalMeta };
|
|
231
|
-
}
|
|
232
|
-
getMetadata(pathname, meta = { "app-source": "user-app" }) {
|
|
233
|
-
const isHtml = pathname.endsWith(".html");
|
|
234
|
-
if (isHtml) {
|
|
235
|
-
meta = {
|
|
236
|
-
...meta,
|
|
237
|
-
"content-type": "text/html; charset=utf-8",
|
|
238
|
-
"cache-control": "no-cache"
|
|
239
|
-
};
|
|
240
|
-
} else {
|
|
241
|
-
meta = {
|
|
242
|
-
...meta,
|
|
243
|
-
"content-type": getContentType(pathname),
|
|
244
|
-
"cache-control": "max-age=31536000, immutable"
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
return meta;
|
|
248
|
-
}
|
|
249
|
-
async copyObject(sourceObject, targetObject) {
|
|
250
|
-
const bucketName = this.bucketName;
|
|
251
|
-
const obj = await this.client.copyObject(bucketName, sourceObject, targetObject);
|
|
252
|
-
return obj;
|
|
253
|
-
}
|
|
254
|
-
async replaceObject(objectName, meta) {
|
|
255
|
-
const { bucketName, client } = this;
|
|
256
|
-
return copyObject({ bucketName, client, objectName: `${this.prefix}${objectName}`, newMetadata: meta });
|
|
257
|
-
}
|
|
258
|
-
static create(opts) {
|
|
259
|
-
return new this(opts);
|
|
260
|
-
}
|
|
261
|
-
static fromBase(createOpts) {
|
|
262
|
-
const base = createOpts.oss;
|
|
263
|
-
const opts = createOpts.opts;
|
|
264
|
-
return new this({
|
|
265
|
-
client: base.client,
|
|
266
|
-
bucketName: base.bucketName,
|
|
267
|
-
...opts
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// src/util/extract-standard-headers.ts
|
|
272
|
-
var standardHeaderKeys = ["content-type", "cache-control", "content-disposition", "content-encoding", "content-language", "expires"];
|
|
273
|
-
function extractStandardHeaders(metaData) {
|
|
274
|
-
const standardHeaders = {};
|
|
275
|
-
const customMetadata = {};
|
|
276
|
-
for (const [key, value] of Object.entries(metaData)) {
|
|
277
|
-
const lowerKey = key.toLowerCase();
|
|
278
|
-
if (lowerKey === "content-type") {
|
|
279
|
-
standardHeaders.ContentType = value;
|
|
280
|
-
} else if (lowerKey === "cache-control") {
|
|
281
|
-
standardHeaders.CacheControl = value;
|
|
282
|
-
} else if (lowerKey === "content-disposition") {
|
|
283
|
-
standardHeaders.ContentDisposition = value;
|
|
284
|
-
} else if (lowerKey === "content-encoding") {
|
|
285
|
-
standardHeaders.ContentEncoding = value;
|
|
286
|
-
} else if (lowerKey === "content-language") {
|
|
287
|
-
standardHeaders.ContentLanguage = value;
|
|
288
|
-
} else if (lowerKey === "expires") {
|
|
289
|
-
standardHeaders.Expires = new Date(value);
|
|
290
|
-
} else {
|
|
291
|
-
customMetadata[key] = value;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
return { standardHeaders, customMetadata };
|
|
295
|
-
}
|
|
296
|
-
// src/services/config.ts
|
|
297
|
-
class ConfigOssService extends OssBase {
|
|
298
|
-
owner;
|
|
299
|
-
constructor(opts) {
|
|
300
|
-
super(opts);
|
|
301
|
-
this.owner = opts.owner;
|
|
302
|
-
this.setPrefix(`${this.owner}/config/1.0.0/`);
|
|
303
|
-
}
|
|
304
|
-
async listAllFile() {
|
|
305
|
-
const list = await this.listObjects("");
|
|
306
|
-
return list.filter((item) => item.size > 0 && this.isEndWithJson(item.name));
|
|
307
|
-
}
|
|
308
|
-
async listAll() {
|
|
309
|
-
return await this.listObjects("");
|
|
310
|
-
}
|
|
311
|
-
configMap = new Map;
|
|
312
|
-
keys = [];
|
|
313
|
-
async getAllConfigJson() {
|
|
314
|
-
const list = await this.listAllFile();
|
|
315
|
-
return list;
|
|
316
|
-
}
|
|
317
|
-
isEndWithJson(string) {
|
|
318
|
-
return string?.endsWith?.(`.json`);
|
|
319
|
-
}
|
|
320
|
-
putJsonObject(key, data) {
|
|
321
|
-
const json = hashSringify(data);
|
|
322
|
-
return this.putObject(key, json, {
|
|
323
|
-
"Content-Type": "application/json",
|
|
324
|
-
"app-source": "user-config-app",
|
|
325
|
-
"Cache-Control": "max-age=31536000, immutable"
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
async getObjectList(objectNameList) {
|
|
329
|
-
const jsonMap = new Map;
|
|
330
|
-
for (const objectName of objectNameList) {
|
|
331
|
-
try {
|
|
332
|
-
const json = await this.getJson(objectName);
|
|
333
|
-
jsonMap.set(objectName, json);
|
|
334
|
-
} catch (error) {
|
|
335
|
-
console.error(error);
|
|
336
|
-
continue;
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
return jsonMap;
|
|
340
|
-
}
|
|
341
|
-
async getList() {
|
|
342
|
-
const list = await this.listAllFile();
|
|
343
|
-
const keyEtagMap = new Map;
|
|
344
|
-
const listKeys = list.map((item) => {
|
|
345
|
-
const key = item.name.replace(this.prefix, "");
|
|
346
|
-
keyEtagMap.set(key, item.etag);
|
|
347
|
-
return {
|
|
348
|
-
...item,
|
|
349
|
-
key
|
|
350
|
-
};
|
|
351
|
-
});
|
|
352
|
-
const keys = Array.from(keyEtagMap.keys());
|
|
353
|
-
return { list: listKeys, keys, keyEtagMap };
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
// src/util/download.ts
|
|
357
|
-
import fs from "node:fs";
|
|
358
|
-
import path2 from "node:path";
|
|
359
|
-
var viewableExtensions = ["jpg", "jpeg", "png", "gif", "svg", "webp", "mp4", "webm", "mp3", "wav", "ogg", "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx"];
|
|
360
|
-
var filterMetaDataKeys = (metaData, clearKeys = []) => {
|
|
361
|
-
const keys = Object.keys(metaData);
|
|
362
|
-
const removeKeys = ["password", "accesskey", "secretkey", ...clearKeys];
|
|
363
|
-
const filteredKeys = keys.filter((key) => !removeKeys.includes(key));
|
|
364
|
-
return filteredKeys.reduce((acc, key) => {
|
|
365
|
-
acc[key] = metaData[key];
|
|
366
|
-
return acc;
|
|
367
|
-
}, {});
|
|
368
|
-
};
|
|
369
|
-
var NotFoundFile = (res, msg, code = 404) => {
|
|
370
|
-
res.writeHead(code, { "Content-Type": "text/plain" });
|
|
371
|
-
res.end(msg || "Not Found File");
|
|
372
|
-
return;
|
|
373
|
-
};
|
|
374
|
-
var sendObject = async ({ res, objectName, client, isDownload = false }) => {
|
|
375
|
-
let stat;
|
|
376
|
-
try {
|
|
377
|
-
stat = await client.statObject(objectName);
|
|
378
|
-
} catch (e) {} finally {
|
|
379
|
-
if (!stat || stat.size === 0) {
|
|
380
|
-
return NotFoundFile(res);
|
|
381
|
-
}
|
|
382
|
-
const contentLength = stat.size;
|
|
383
|
-
const etag = stat.etag;
|
|
384
|
-
const lastModified = stat.lastModified.toISOString();
|
|
385
|
-
const filename = objectName.split("/").pop() || "no-file-name-download";
|
|
386
|
-
const fileExtension = filename.split(".").pop()?.toLowerCase() || "";
|
|
387
|
-
const filteredMetaData = filterMetaDataKeys(stat.metaData, ["size", "etag", "last-modified"]);
|
|
388
|
-
const contentDisposition = viewableExtensions.includes(fileExtension) && !isDownload ? "inline" : `attachment; filename="${filename}"`;
|
|
389
|
-
res.writeHead(200, {
|
|
390
|
-
"Content-Length": contentLength,
|
|
391
|
-
etag,
|
|
392
|
-
"last-modified": lastModified,
|
|
393
|
-
"Content-Disposition": contentDisposition,
|
|
394
|
-
...filteredMetaData
|
|
395
|
-
});
|
|
396
|
-
const objectStream = await client.getObject(objectName);
|
|
397
|
-
objectStream.pipe(res, { end: true });
|
|
398
|
-
}
|
|
399
|
-
};
|
|
400
|
-
var downloadObject = async ({ objectName, client, filePath }) => {
|
|
401
|
-
const objectStream = await client.getObject(objectName);
|
|
402
|
-
const dir = path2.dirname(filePath);
|
|
403
|
-
if (!fs.existsSync(dir)) {
|
|
404
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
405
|
-
}
|
|
406
|
-
objectStream.pipe(fs.createWriteStream(filePath));
|
|
407
|
-
return objectStream;
|
|
408
|
-
};
|
|
409
|
-
export {
|
|
410
|
-
standardHeaderKeys,
|
|
411
|
-
sendObject,
|
|
412
|
-
hashSringify,
|
|
413
|
-
hash,
|
|
414
|
-
getContentType,
|
|
415
|
-
filterMetaDataKeys,
|
|
416
|
-
extractStandardHeaders,
|
|
417
|
-
downloadObject,
|
|
418
|
-
OssBase,
|
|
419
|
-
NotFoundFile,
|
|
420
|
-
ConfigOssService
|
|
421
|
-
};
|
package/src/core/copy-object.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Client, CopyDestinationOptions, CopySourceOptions } from 'minio';
|
|
2
|
-
|
|
3
|
-
type CopyObjectOpts = {
|
|
4
|
-
bucketName: string;
|
|
5
|
-
newMetadata: Record<string, string>;
|
|
6
|
-
objectName: string;
|
|
7
|
-
client: Client;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* 复制对象 REPLACE 替换
|
|
11
|
-
* @param param0
|
|
12
|
-
* @returns
|
|
13
|
-
*/
|
|
14
|
-
export const copyObject = async ({ bucketName, newMetadata, objectName, client }: CopyObjectOpts) => {
|
|
15
|
-
const source = new CopySourceOptions({ Bucket: bucketName, Object: objectName });
|
|
16
|
-
const stat = await client.statObject(bucketName, objectName);
|
|
17
|
-
const sourceMetadata = stat.metaData;
|
|
18
|
-
const destination = new CopyDestinationOptions({
|
|
19
|
-
Bucket: bucketName,
|
|
20
|
-
Object: objectName,
|
|
21
|
-
UserMetadata: { ...sourceMetadata, ...newMetadata },
|
|
22
|
-
MetadataDirective: 'REPLACE',
|
|
23
|
-
});
|
|
24
|
-
const copyResult = await client.copyObject(source, destination);
|
|
25
|
-
return copyResult;
|
|
26
|
-
};
|