fsd-oss 0.10.1 → 0.11.1

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/lib/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const Path = require("path");
4
- const OSS = require("ali-oss");
5
4
  const slash = require("slash");
6
5
  const minimatch = require("minimatch");
7
6
  const Debugger = require("debug");
8
7
  const eachLimit = require("async/eachLimit");
9
8
  const RPC = require("@alicloud/pop-core");
9
+ const simple_oss_client_1 = require("./simple-oss-client");
10
10
  const stream_1 = require("stream");
11
11
  const debug = Debugger('fsd-oss');
12
12
  const CALLBACK_BODY = 'bucket=${bucket}&path=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}&format=${imageInfo.format}';
@@ -28,14 +28,12 @@ class OSSAdapter {
28
28
  if (options.endpoint)
29
29
  throw new Error('fsd-oss options "endpoint" has been deprecated, please use region/[internal]/[secure] instead!');
30
30
  this._options = options;
31
- this._oss = new OSS({
31
+ this._oss = new simple_oss_client_1.default({
32
32
  accessKeyId: options.accessKeyId,
33
33
  accessKeySecret: options.accessKeySecret,
34
34
  bucket: options.bucket,
35
- region: options.region,
36
- internal: options.internal,
37
- secure: options.secure,
38
- timeout: options.timeout
35
+ endpoint: `https://${options.region}${options.internal ? '-internal' : ''}.aliyuncs.com`,
36
+ timeout: parseInt(options.timeout) || 0
39
37
  });
40
38
  if (options.accountId && options.roleName) {
41
39
  let stsEndpoint = 'https://sts.aliyuncs.com';
@@ -52,7 +50,7 @@ class OSSAdapter {
52
50
  this.createUploadToken = async (path, meta) => {
53
51
  if (!options.accountId || !options.roleName)
54
52
  throw new Error('Can not create sts token, missing options: accountId and roleName!');
55
- path = slash(Path.join(options.root, path)).substr(1);
53
+ path = slash(Path.join(options.root, path)).substring(1);
56
54
  let params = {
57
55
  RoleArn: `acs:ram::${options.accountId}:role/${options.roleName}`,
58
56
  RoleSessionName: 'fsd',
@@ -110,21 +108,21 @@ class OSSAdapter {
110
108
  async append(path, data) {
111
109
  debug('append %s', path);
112
110
  const { root } = this._options;
113
- let p = slash(Path.join(root, path)).substr(1);
111
+ let p = slash(Path.join(root, path)).substring(1);
114
112
  if (typeof data === 'string') {
115
113
  data = Buffer.from(data);
116
114
  }
117
- let options = {};
115
+ let position = 0;
118
116
  try {
119
- options.position = await this.size(path);
117
+ position = await this.size(path);
120
118
  }
121
119
  catch (e) { }
122
- await this._oss.append(p, data, options);
120
+ await this._oss.append(p, data, { position });
123
121
  }
124
122
  async createReadStream(path, options) {
125
123
  debug('createReadStream %s options: %o', path, options);
126
124
  const { root } = this._options;
127
- let p = slash(Path.join(root, path)).substr(1);
125
+ let p = slash(Path.join(root, path)).substring(1);
128
126
  let opts = {};
129
127
  if (options) {
130
128
  let start = options.start || 0;
@@ -140,45 +138,42 @@ class OSSAdapter {
140
138
  opts.headers = { Range: `bytes=${Range}` };
141
139
  }
142
140
  }
143
- let res = await this._oss.getStream(p, opts);
144
- if (!res || !res.stream)
145
- throw new Error('no stream');
146
- return res.stream;
141
+ return (await this._oss.get(p, opts).stream());
147
142
  }
148
143
  async createWriteStream(path, options) {
149
144
  debug('createWriteStream %s', path);
150
145
  if (options === null || options === void 0 ? void 0 : options.start)
151
146
  throw new Error('fsd-oss read stream does not support start options');
152
147
  const { root } = this._options;
153
- let p = slash(Path.join(root, path)).substr(1);
148
+ let p = slash(Path.join(root, path)).substring(1);
154
149
  let stream = new stream_1.PassThrough();
155
- stream.promise = this._oss.putStream(p, stream);
150
+ stream.promise = this._oss.put(p, stream);
156
151
  return stream;
157
152
  }
158
153
  async unlink(path) {
159
154
  var _a;
160
155
  debug('unlink %s', path);
161
156
  const { root } = this._options;
162
- let p = slash(Path.join(root, path)).substr(1);
157
+ let p = slash(Path.join(root, path)).substring(1);
163
158
  if (path.endsWith('/')) {
164
- let nextMarker = '';
159
+ let continuationToken = '';
165
160
  do {
166
161
  let list = await this._oss.list({
167
162
  prefix: p,
168
- marker: nextMarker,
169
- 'max-keys': 1000
170
- }, {});
171
- ({ nextMarker } = list);
172
- if ((_a = list.objects) === null || _a === void 0 ? void 0 : _a.length) {
173
- let objects = list.objects.map((o) => o.name);
163
+ continuationToken,
164
+ maxKeys: 1000
165
+ });
166
+ continuationToken = list.NextContinuationToken;
167
+ if ((_a = list.Contents) === null || _a === void 0 ? void 0 : _a.length) {
168
+ let objects = list.Contents.map((o) => o.Key);
174
169
  await this._oss.deleteMulti(objects, {
175
170
  quiet: true
176
171
  });
177
172
  }
178
- } while (nextMarker);
173
+ } while (continuationToken);
179
174
  }
180
175
  else {
181
- await this._oss.delete(p);
176
+ await this._oss.del(p);
182
177
  }
183
178
  }
184
179
  async mkdir(path, recursive) {
@@ -192,7 +187,7 @@ class OSSAdapter {
192
187
  }
193
188
  }
194
189
  const { root } = this._options;
195
- let p = slash(Path.join(root, path)).substr(1);
190
+ let p = slash(Path.join(root, path)).substring(1);
196
191
  let res = await this._oss.put(p, Buffer.from(''));
197
192
  debug('mkdir result: %O', res);
198
193
  }
@@ -207,52 +202,37 @@ class OSSAdapter {
207
202
  pattern = recursion;
208
203
  }
209
204
  const { root } = this._options;
210
- let p = slash(Path.join(root, path)).substr(1);
205
+ let p = slash(Path.join(root, path)).substring(1);
211
206
  let results = [];
212
- let nextMarker = '';
207
+ let continuationToken = '';
213
208
  do {
214
209
  let list = await this._oss.list({
215
210
  prefix: p,
216
211
  delimiter,
217
- marker: nextMarker,
218
- 'max-keys': 1000
219
- }, {});
212
+ continuationToken,
213
+ maxKeys: 1000
214
+ });
220
215
  debug('list: %O', list);
221
- ({ nextMarker } = list);
222
- if (list.prefixes) {
223
- list.prefixes.forEach((name) => {
224
- let relative = slash(Path.relative(p, name));
225
- if (!relative)
226
- return;
227
- results.push({
228
- name: `${relative}/`,
229
- metadata: {
230
- size: 0,
231
- lastModified: null
232
- }
233
- });
234
- });
235
- }
236
- if (list.objects) {
237
- list.objects.forEach((object) => {
238
- let { name } = object;
239
- let relative = slash(Path.relative(p, name));
216
+ continuationToken = list.NextContinuationToken;
217
+ if (list.Contents) {
218
+ list.Contents.forEach((object) => {
219
+ let relative = slash(Path.relative(p, object.Key));
240
220
  if (!relative)
241
221
  return;
242
- if (name.endsWith('/'))
222
+ if (object.Key.endsWith('/'))
243
223
  relative += '/';
244
224
  if (pattern && pattern !== '**/*' && !minimatch(relative, pattern))
245
225
  return;
246
226
  results.push({
247
227
  name: relative,
248
228
  metadata: {
249
- size: object.size,
250
- lastModified: new Date(object.lastModified)
229
+ size: object.Size,
230
+ lastModified: new Date(object.LastModified)
251
231
  }
252
232
  });
253
233
  });
254
234
  }
255
- } while (nextMarker);
235
+ } while (continuationToken);
256
236
  return results;
257
237
  }
258
238
  async createUrl(path, options) {
@@ -262,7 +242,7 @@ class OSSAdapter {
262
242
  if (urlPrefix && publicRead) {
263
243
  return urlPrefix + p;
264
244
  }
265
- let url = this._oss.signatureUrl(p.substr(1), options);
245
+ let url = this._oss.signatureUrl(p.substring(1), options);
266
246
  if (urlPrefix) {
267
247
  url = url.replace(/https?\:\/\/[^/]+/, urlPrefix);
268
248
  }
@@ -274,29 +254,28 @@ class OSSAdapter {
274
254
  if (!(await this.exists(path)))
275
255
  throw new Error('The source path is not exists!');
276
256
  const { root } = this._options;
277
- let from = slash(Path.join(root, path)).substr(1);
278
- let to = slash(Path.join(root, dest)).substr(1);
257
+ let from = slash(Path.join(root, path)).substring(1);
258
+ let to = slash(Path.join(root, dest)).substring(1);
279
259
  if (path.endsWith('/')) {
280
260
  debug('copy directory %s -> %s', from, to);
281
- let nextMarker = '';
261
+ let continuationToken = '';
282
262
  do {
283
263
  let list = await this._oss.list({
284
264
  prefix: from,
285
- marker: nextMarker,
286
- 'max-keys': 1000
287
- }, {});
265
+ continuationToken,
266
+ maxKeys: 1000
267
+ });
288
268
  debug('list result: %O', list);
289
- ({ nextMarker } = list);
290
- if ((_a = list.objects) === null || _a === void 0 ? void 0 : _a.length) {
291
- await eachLimit(list.objects, 10, async (object) => {
292
- let { name } = object;
293
- debug(' -> copy %s', name);
294
- let relative = slash(Path.relative(from, name));
269
+ continuationToken = list.NextContinuationToken;
270
+ if ((_a = list.Contents) === null || _a === void 0 ? void 0 : _a.length) {
271
+ await eachLimit(list.Contents, 10, async (object) => {
272
+ debug(' -> copy %s', object.Key);
273
+ let relative = slash(Path.relative(from, object.Key));
295
274
  let target = slash(Path.join(to, relative));
296
- await this._oss.copy(target, name);
275
+ await this._oss.copy(target, object.Key);
297
276
  });
298
277
  }
299
- } while (nextMarker);
278
+ } while (continuationToken);
300
279
  }
301
280
  else {
302
281
  debug('copy file %s -> %s', from, to);
@@ -315,13 +294,13 @@ class OSSAdapter {
315
294
  async exists(path) {
316
295
  debug('check exists %s', path);
317
296
  const { root } = this._options;
318
- let p = slash(Path.join(root, path)).substr(1);
297
+ let p = slash(Path.join(root, path)).substring(1);
319
298
  if (path.endsWith('/')) {
320
299
  let list = await this._oss.list({
321
300
  prefix: p,
322
- 'max-keys': 1
323
- }, {});
324
- return list.objects && list.objects.length > 0;
301
+ maxKeys: 1
302
+ });
303
+ return list.Contents && list.Contents.length > 0;
325
304
  }
326
305
  try {
327
306
  await this._oss.head(p);
@@ -334,7 +313,7 @@ class OSSAdapter {
334
313
  async isFile(path) {
335
314
  debug('check is file %s', path);
336
315
  const { root } = this._options;
337
- let p = slash(Path.join(root, path)).substr(1);
316
+ let p = slash(Path.join(root, path)).substring(1);
338
317
  try {
339
318
  await this._oss.head(p);
340
319
  return true;
@@ -345,7 +324,7 @@ class OSSAdapter {
345
324
  }
346
325
  async isDirectory(path) {
347
326
  debug('check is directory %s', path);
348
- let p = slash(Path.join(this._options.root, path)).substr(1);
327
+ let p = slash(Path.join(this._options.root, path)).substring(1);
349
328
  try {
350
329
  await this._oss.head(p);
351
330
  return true;
@@ -356,30 +335,24 @@ class OSSAdapter {
356
335
  }
357
336
  async size(path) {
358
337
  debug('get file size %s', path);
359
- let p = slash(Path.join(this._options.root, path)).substr(1);
360
- let list = await this._oss.list({
361
- prefix: p,
362
- 'max-keys': 1
363
- }, {});
364
- if (!list.objects || !list.objects.length || list.objects[0].name !== p)
365
- throw new Error(`${path} is not exist!`);
366
- return list.objects[0].size;
338
+ let p = slash(Path.join(this._options.root, path)).substring(1);
339
+ let res = await this._oss.head(p);
340
+ return parseInt(res.headers.get('Content-Length')) || 0;
367
341
  }
368
342
  async lastModified(path) {
369
343
  debug('get file lastModified %s', path);
370
- let p = slash(Path.join(this._options.root, path)).substr(1);
344
+ let p = slash(Path.join(this._options.root, path)).substring(1);
371
345
  let res = await this._oss.head(p);
372
- let headers = res.res.headers;
373
- return new Date(headers['last-modified'] || headers.date);
346
+ let headers = res.headers;
347
+ return new Date(headers.get('Last-Modified'));
374
348
  }
375
349
  async initMultipartUpload(path, partCount) {
376
350
  debug('initMultipartUpload %s, partCount: %d', path, partCount);
377
- let p = slash(Path.join(this._options.root, path)).substr(1);
378
- let res = await this._oss.initMultipartUpload(p);
379
- let { uploadId } = res;
351
+ let p = slash(Path.join(this._options.root, path)).substring(1);
352
+ let { UploadId } = await this._oss.initMultipartUpload(p);
380
353
  let files = [];
381
354
  for (let i = 1; i <= partCount; i += 1) {
382
- files.push(`task://${uploadId}?${i}`);
355
+ files.push(`task://${UploadId}?${i}`);
383
356
  }
384
357
  return files;
385
358
  }
@@ -389,11 +362,12 @@ class OSSAdapter {
389
362
  if (!partTask.startsWith('task://'))
390
363
  throw new Error('Invalid part task id');
391
364
  let [uploadId, no] = partTask.replace('task://', '').split('?');
392
- let res = await this._oss._uploadPart(p, uploadId, parseInt(no), {
393
- stream: data,
394
- size
365
+ let res = await this._oss.uploadPart(p, uploadId, parseInt(no), data, {
366
+ headers: {
367
+ 'Content-Length': String(size)
368
+ }
395
369
  });
396
- let etag = res.etag.replace(/"/g, '');
370
+ let etag = res.headers.get('ETag').replace(/"/g, '');
397
371
  return `${partTask.replace('task://', 'part://')}#${etag}`;
398
372
  }
399
373
  async completeMultipartUpload(path, parts) {
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const qs = require("qs");
4
+ const xmljs = require("xml-js");
5
+ const sha1 = require("crypto-js/hmac-sha1");
6
+ const md5 = require("crypto-js/md5");
7
+ const Base64Encoder = require("crypto-js/enc-base64");
8
+ const mime = require("mime-types");
9
+ const akita_1 = require("akita");
10
+ const client = akita_1.default.create({});
11
+ class SimpleOSSClient {
12
+ constructor(config) {
13
+ this.config = config;
14
+ if (/^https?:/.test(config.endpoint)) {
15
+ let url = new URL(config.endpoint);
16
+ this.endpoint = url.host;
17
+ }
18
+ else {
19
+ this.endpoint = config.endpoint;
20
+ }
21
+ }
22
+ append(name, body, options) {
23
+ options = options || {};
24
+ let position = options.position || 0;
25
+ if (!options.mime)
26
+ options.mime = mime.lookup(name) || 'application/octet-stream';
27
+ return this.requestData('POST', `${name}?append&position=${position}`, body, options);
28
+ }
29
+ get(name, options) {
30
+ return this.request('GET', name, null, options);
31
+ }
32
+ put(name, body, options) {
33
+ options = options || {};
34
+ if (!options.mime)
35
+ options.mime = mime.lookup(name) || 'application/octet-stream';
36
+ return this.requestData('PUT', name, body, options);
37
+ }
38
+ async copy(to, from, options) {
39
+ options = options || {};
40
+ if (!options.headers)
41
+ options.headers = {};
42
+ options.headers['x-oss-copy-source'] = `/${this.config.bucket}/${from}`;
43
+ let res = await this.requestData('PUT', to, null, options);
44
+ res = Object.assign({ headers: res.headers }, res.CopyObjectResult);
45
+ return res;
46
+ }
47
+ async head(name, options) {
48
+ let response = await this.request('HEAD', name, null, options).response();
49
+ if (response.status === 404) {
50
+ throw new Error('NoSuchKey');
51
+ }
52
+ if (response.status !== 200) {
53
+ throw new Error(response.statusText);
54
+ }
55
+ return { headers: response.headers };
56
+ }
57
+ async list(options) {
58
+ let query = {
59
+ 'list-type': '2'
60
+ };
61
+ if (options.prefix)
62
+ query.prefix = options.prefix;
63
+ if (options.delimiter)
64
+ query.delimiter = options.delimiter;
65
+ if (options.startAfter)
66
+ query['start-after'] = options.startAfter;
67
+ if (options.continuationToken)
68
+ query['continuation-token'] = options.continuationToken;
69
+ if (options.maxKeys)
70
+ query['max-keys'] = options.maxKeys;
71
+ if (options.encodingType)
72
+ query['encoding-type'] = options.encodingType;
73
+ if (options.fetchOwner)
74
+ query['fetch-owner'] = options.fetchOwner;
75
+ options.query = query;
76
+ let res = await this.requestData('GET', '', null, options);
77
+ res = Object.assign({ headers: res.headers }, res.ListBucketResult);
78
+ res.KeyCount = parseInt(res.KeyCount);
79
+ res.MaxKeys = parseInt(res.MaxKeys);
80
+ res.IsTruncated = res.IsTruncated === 'true';
81
+ res.Prefix = res.Prefix || '';
82
+ if (res.Contents) {
83
+ if (!Array.isArray(res.Contents))
84
+ res.Contents = [res.Contents];
85
+ res.Contents.forEach((content) => {
86
+ content.Size = parseInt(content.Size);
87
+ });
88
+ }
89
+ return res;
90
+ }
91
+ del(name, options) {
92
+ options = options || {};
93
+ if (!options.mime)
94
+ options.mime = mime.lookup(name) || 'application/octet-stream';
95
+ return this.requestData('DELETE', name, null, options);
96
+ }
97
+ async deleteMulti(names, options) {
98
+ options = options || {};
99
+ let body = `<?xml version="1.0" encoding="UTF-8"?><Delete><Quiet>${options.quiet || false}</Quiet>${names.map((name) => `<Object><Key>${name}</Key></Object>`).join('')}</Delete>`;
100
+ if (!options.mime)
101
+ options.mime = 'application/xml';
102
+ if (!options.headers)
103
+ options.headers = {};
104
+ options.headers['Content-MD5'] = md5(body).toString(Base64Encoder);
105
+ let res = await this.requestData('POST', '?delete', body, options);
106
+ res = Object.assign({ headers: res.headers }, res.DeleteResult);
107
+ if (res.Deleted) {
108
+ if (!Array.isArray(res.Deleted))
109
+ res.Deleted = [res.Deleted];
110
+ res.Deleted.forEach((deleted) => {
111
+ if (deleted.DeleteMarker)
112
+ deleted.DeleteMarker = deleted.DeleteMarker === 'true';
113
+ });
114
+ }
115
+ return res;
116
+ }
117
+ async initMultipartUpload(name, options) {
118
+ options = options || {};
119
+ if (!options.mime)
120
+ options.mime = mime.lookup(name) || 'application/octet-stream';
121
+ let res = await this.requestData('POST', `${name}?uploads`, '', options);
122
+ return Object.assign({ headers: res.headers }, res.InitiateMultipartUploadResult);
123
+ }
124
+ uploadPart(name, uploadId, partNumber, body, options) {
125
+ options = options || {};
126
+ if (!options.mime)
127
+ options.mime = mime.lookup(name) || 'application/octet-stream';
128
+ return this.requestData('PUT', `${name}?partNumber=${partNumber}&uploadId=${uploadId}`, body, options);
129
+ }
130
+ async completeMultipartUpload(name, uploadId, parts, options) {
131
+ options = options || {};
132
+ options.mime = 'application/xml';
133
+ let body = `<?xml version="1.0" encoding="UTF-8"?>\n<CompleteMultipartUpload>${parts
134
+ .map((part) => `<Part><PartNumber>${part.number}</PartNumber><ETag>${part.etag}</ETag></Part>`)
135
+ .join('')}</CompleteMultipartUpload>`;
136
+ let res = await this.requestData('POST', `${name}?uploadId=${uploadId}`, body, options);
137
+ return Object.assign({ headers: res.headers }, res.CompleteMultipartUploadResult);
138
+ }
139
+ request(method, resource, body, options) {
140
+ options = options || {};
141
+ let headers = options.headers || {};
142
+ if (options.mime) {
143
+ headers['Content-Type'] = options.mime;
144
+ }
145
+ if (options.meta) {
146
+ Object.keys(options.meta).forEach((key) => {
147
+ headers[`x-oss-meta-${key}`] = String(options.meta[key]);
148
+ });
149
+ }
150
+ headers['x-oss-date'] = new Date().toUTCString();
151
+ if (this.config.stsToken) {
152
+ headers['x-oss-security-token'] = this.config.stsToken;
153
+ }
154
+ headers.authorization = this.getSign(method, `/${this.config.bucket}/${resource}`, headers);
155
+ let url = `https://${this.config.bucket}.${this.endpoint}/${resource}`;
156
+ return client.request(url, {
157
+ method,
158
+ headers,
159
+ query: options.query,
160
+ body,
161
+ timeout: options.timeout || this.config.timeout || 60000
162
+ });
163
+ }
164
+ async requestData(method, resource, body, options) {
165
+ let response = await this.request(method, resource, body, options).response();
166
+ let xml = await response.text();
167
+ if (!xml && response.status === 200) {
168
+ return {
169
+ headers: response.headers
170
+ };
171
+ }
172
+ let data = xmljs.xml2js(xml, { compact: true });
173
+ if (data.Error) {
174
+ throw new Error(data.Error.Message._text);
175
+ }
176
+ let res = Object.assign({ headers: response.headers }, conventXmlObject(data));
177
+ delete res._declaration;
178
+ return res;
179
+ }
180
+ signatureUrl(name, options) {
181
+ options = options || {};
182
+ let expires = parseInt((Date.now() / 1000 + (options.expires || 3600)));
183
+ let response = options.response || {};
184
+ let url = `https://${this.config.bucket}.${this.endpoint}/${name}`;
185
+ let query = {};
186
+ if (options.trafficLimit) {
187
+ query['x-oss-traffic-limit'] = options.trafficLimit;
188
+ }
189
+ if (options.response) {
190
+ Object.keys(response).forEach((key) => {
191
+ query[`response-${key}`] = response[key];
192
+ });
193
+ }
194
+ if (this.config.stsToken) {
195
+ query['security-token'] = this.config.stsToken;
196
+ }
197
+ let parts = [options.method || 'GET', '', '', String(expires)];
198
+ let string = qs.stringify(query);
199
+ parts.push(`/${this.config.bucket}/${name}${string ? `?${string}` : ''}`);
200
+ query.Signature = sha1(parts.join('\n'), this.config.accessKeySecret).toString(Base64Encoder);
201
+ query.OSSAccessKeyId = this.config.accessKeyId;
202
+ query.Expires = expires;
203
+ return `${url}?${qs.stringify(query)}`;
204
+ }
205
+ getSign(method, canonicalizedResource, headers) {
206
+ headers = headers || {};
207
+ let parts = [
208
+ method,
209
+ headers['Content-MD5'] || '',
210
+ headers['Content-Type'] || '',
211
+ headers['x-oss-date'] || new Date().toUTCString()
212
+ ];
213
+ Object.keys(headers)
214
+ .sort()
215
+ .forEach((key) => {
216
+ if (key.startsWith('x-oss-'))
217
+ parts.push(`${key}:${headers[key]}`);
218
+ });
219
+ parts.push(canonicalizedResource);
220
+ let sign = sha1(parts.join('\n'), this.config.accessKeySecret).toString(Base64Encoder);
221
+ return `OSS ${this.config.accessKeyId}:${sign}`;
222
+ }
223
+ }
224
+ exports.default = SimpleOSSClient;
225
+ function conventXmlObject(object) {
226
+ if (Array.isArray(object))
227
+ return object.map(conventXmlObject);
228
+ if (object && typeof object === 'object') {
229
+ let keys = Object.keys(object);
230
+ if (keys.length === 1 && keys[0] === '_text')
231
+ return object._text;
232
+ let res = {};
233
+ for (let key of keys) {
234
+ res[key] = conventXmlObject(object[key]);
235
+ }
236
+ return res;
237
+ }
238
+ return object;
239
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fsd-oss",
3
- "version": "0.10.1",
3
+ "version": "0.11.1",
4
4
  "description": "Aliyun OSS adapter for fsd",
5
5
  "main": "lib/index.js",
6
6
  "types": "index.d.ts",
@@ -13,11 +13,14 @@
13
13
  "license": "MIT",
14
14
  "dependencies": {
15
15
  "@alicloud/pop-core": "^1.7.12",
16
- "ali-oss": "^6.17.1",
16
+ "akita": "^1.0.4",
17
17
  "async": "*",
18
+ "crypto-js": "^4.1.1",
18
19
  "debug": "^4.3.4",
20
+ "mime-types": "^2.1.35",
19
21
  "minimatch": "^3.1.2",
20
- "slash": "^3.0.0"
22
+ "slash": "^3.0.0",
23
+ "xml-js": "^1.6.11"
21
24
  },
22
- "gitHead": "0e15db87212337e709c07bdc9c855132bb347db9"
25
+ "gitHead": "5f39a56e4f5ae28e6f15c7888413561a924b54de"
23
26
  }
@@ -0,0 +1,167 @@
1
+ import { Request } from 'akita';
2
+
3
+ export default class SimpleOSSClient {
4
+ config: SimpleOSSClientConfig;
5
+ endpoint: string;
6
+
7
+ constructor(config: SimpleOSSClientConfig);
8
+
9
+ append(name: string, body: any, options?: AppendOptions): Promise<Result>;
10
+
11
+ get(name: string, options?: RequestOptions): Request<any>;
12
+
13
+ put(name: string, body: any, options?: RequestOptions): Promise<Result>;
14
+
15
+ copy(to: string, from: string, options?: RequestOptions): Promise<CopyResult>;
16
+
17
+ head(name: string, options?: RequestOptions): Promise<Result>;
18
+
19
+ list(options: ListOptions): Promise<ListResult>;
20
+
21
+ del(name: string, options?: RequestOptions): Promise<Result>;
22
+
23
+ deleteMulti(names: string[], options?: DeleteMultiOptions): Promise<DeleteMultiResult>;
24
+
25
+ initMultipartUpload(
26
+ name: string,
27
+ options?: RequestOptions
28
+ ): Promise<InitiateMultipartUploadResult>;
29
+
30
+ uploadPart(
31
+ name: string,
32
+ uploadId: string,
33
+ partNumber: number,
34
+ body: any,
35
+ options?: RequestOptions
36
+ ): Promise<Result>;
37
+
38
+ completeMultipartUpload(
39
+ name: string,
40
+ uploadId: string,
41
+ parts: Array<{ number: number; etag: string }>,
42
+ options?: RequestOptions
43
+ ): Promise<CompleteMultipartUploadResult>;
44
+
45
+ request(method: string, resource: string, body?: any, options?: RequestOptions): Request<any>;
46
+
47
+ requestData(method: string, resource: string, body?: any, options?: RequestOptions): Promise<any>;
48
+
49
+ signatureUrl(name: string, options?: SignatureUrlOptions): string;
50
+ }
51
+
52
+ export interface SimpleOSSClientConfig {
53
+ accessKeyId: string;
54
+ accessKeySecret: string;
55
+ bucket: string;
56
+ endpoint: string;
57
+ stsToken?: string;
58
+ timeout?: number;
59
+ }
60
+
61
+ export interface RequestOptions {
62
+ timeout?: number;
63
+ mime?: string;
64
+ meta?: UserMeta;
65
+ headers?: Record<string, string>;
66
+ query?: Record<string, string>;
67
+ }
68
+
69
+ export interface Result {
70
+ headers: Headers;
71
+ }
72
+
73
+ export interface UserMeta extends Record<string, string | number> {
74
+ uid: number;
75
+ pid: number;
76
+ }
77
+
78
+ export interface AppendOptions extends RequestOptions {
79
+ position?: number;
80
+ }
81
+
82
+ export interface CopyResult extends Result {
83
+ ETag: string;
84
+ LastModified: string;
85
+ }
86
+
87
+ export interface ListOptions extends RequestOptions {
88
+ prefix?: string;
89
+ delimiter?: string;
90
+ startAfter?: string;
91
+ continuationToken?: string;
92
+ maxKeys?: number;
93
+ encodingType?: string;
94
+ fetchOwner?: boolean;
95
+ }
96
+
97
+ export interface ListResult extends Result {
98
+ Name: string;
99
+ Prefix: string;
100
+ StartAfter: string;
101
+ MaxKeys: number;
102
+ EncodingType: string;
103
+ IsTruncated: boolean;
104
+ KeyCount: number;
105
+ NextContinuationToken?: string;
106
+ Contents: ListResultContent[];
107
+ }
108
+
109
+ export interface ListResultContent {
110
+ Key: string;
111
+ LastModified: string;
112
+ ETag: string;
113
+ Size: number;
114
+ StorageClass: string;
115
+ Owner: {
116
+ ID: string;
117
+ DisplayName: string;
118
+ };
119
+ }
120
+
121
+ export interface DeleteMultiOptions extends RequestOptions {
122
+ quiet?: boolean;
123
+ }
124
+
125
+ export interface DeleteMultiResult extends Result {
126
+ Deleted: Deleted[];
127
+ }
128
+
129
+ export interface Deleted {
130
+ Key: string;
131
+ DeleteMarker?: boolean;
132
+ DeleteMarkerVersionId?: string;
133
+ VersionId?: string;
134
+ }
135
+
136
+ export interface InitiateMultipartUploadResult extends Result {
137
+ Bucket: string;
138
+ Key: string;
139
+ UploadId: string;
140
+ }
141
+
142
+ export interface CompleteMultipartUploadResult extends Result {
143
+ EncodingType: string;
144
+ Location: string;
145
+ Bucket: string;
146
+ Key: string;
147
+ ETag: string;
148
+ }
149
+
150
+ export interface SignatureUrlOptions {
151
+ /**
152
+ * 连接过期时间,单位秒,默认为 1800 秒
153
+ */
154
+ expires?: number;
155
+ /**
156
+ * 请求方法,默认为 GET
157
+ */
158
+ method?: string;
159
+ /**
160
+ * 限速 x-oss-traffic-limit
161
+ */
162
+ trafficLimit?: number;
163
+ /**
164
+ * 返回头信息
165
+ */
166
+ response?: Record<string, string>;
167
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = require('./lib/simple-oss-client').default;