1 | import {escapeName, isHttpsProtocol, objectName} from "./utils";
|
2 | import {rstr2b64, utf8Encode} from "./utils/hash/utils";
|
3 | import request, {RequestOptions, RequestResult, UploadProgressEvent} from "./utils/request";
|
4 | import {computeSignature} from "./utils/signUtils";
|
5 |
|
6 | function getOssParams(params, extendKeys=[]){
|
7 | const out = {};
|
8 | Object.keys(params).forEach(key => {
|
9 | const lowerKey = key.toLowerCase();
|
10 | if (/^x-oss-/.test(lowerKey) || extendKeys.indexOf(key)>-1){
|
11 | out[lowerKey] = params[key];
|
12 | }
|
13 | });
|
14 | return out;
|
15 | }
|
16 |
|
17 |
|
18 | type Region = 'oss-cn-hangzhou' | 'oss-cn-shanghai' | 'oss-cn-qingdao' | 'oss-cn-beijing' | 'oss-cn-zhangjiakou'
|
19 | | 'oss-cn-huhehaote' | 'oss-cn-shenzhen' | 'oss-cn-heyuan' | 'oss-cn-chengdu' | 'oss-cn-hongkong' | 'oss-us-west-1'
|
20 | | 'oss-us-east-1' | 'oss-ap-southeast-1' | 'oss-ap-southeast-2' | 'oss-ap-southeast-3' | 'oss-ap-southeast-5'
|
21 | | 'oss-ap-northeast-1' | 'oss-ap-south-1' | 'oss-eu-central-1' | 'oss-eu-west-1' | 'oss-me-east-1' | string;
|
22 |
|
23 | export interface ClientOptions {
|
24 | |
25 |
|
26 |
|
27 | accessKeyId: string;
|
28 | |
29 |
|
30 |
|
31 | accessKeySecret: string;
|
32 | bucket?: string;
|
33 | |
34 |
|
35 |
|
36 | stsToken?: string;
|
37 | |
38 |
|
39 |
|
40 | endpoint?: string;
|
41 | |
42 |
|
43 |
|
44 | region?: Region;
|
45 | |
46 |
|
47 |
|
48 |
|
49 | internal?: boolean;
|
50 | |
51 |
|
52 |
|
53 | timeout?: number;
|
54 | secure?: boolean;
|
55 | |
56 |
|
57 |
|
58 | cname?: boolean;
|
59 |
|
60 | }
|
61 |
|
62 | export interface PostObjectOptions {
|
63 | dir?: string;
|
64 | policy?: string | {expiration: string; conditions: any[]};
|
65 | signature?: string;
|
66 | timeout?: number;
|
67 | onProgress?: (e: UploadProgressEvent) => void;
|
68 | onSuccess?: (result: RequestResult, xhr: XMLHttpRequest) => void;
|
69 | onError?: (e: Error) => void;
|
70 | onAbort?: () => void;
|
71 | success_action_status?: 200|201|204;
|
72 | success_action_redirect?: string;
|
73 | 'x-oss-object-acl'?: 'private' | 'public-read' | 'public-read-write';
|
74 | headers?: {[key: string]: string | number},
|
75 | [key: string]: any;
|
76 | }
|
77 |
|
78 | class Client {
|
79 | protected opts: ClientOptions & { host: string };
|
80 |
|
81 | constructor(options: ClientOptions) {
|
82 | if (!(this instanceof Client)) {
|
83 | return new Client(options);
|
84 | }
|
85 | const opts = {
|
86 | endpoint: null,
|
87 | region: 'oss-cn-hangzhou',
|
88 | timeout: 300_000,
|
89 | internal: false,
|
90 |
|
91 | secure: undefined,
|
92 | ...options,
|
93 | host: '',
|
94 | };
|
95 | if (opts.endpoint) {
|
96 |
|
97 | } else if (opts.region && opts.bucket) {
|
98 | opts.endpoint = opts.bucket;
|
99 | if (opts.internal) opts.region += '-internal';
|
100 | opts.endpoint += `.${opts.region}.aliyuncs.com`
|
101 | } else {
|
102 | throw new Error('require endpoint or region/bucket in options');
|
103 | }
|
104 |
|
105 | opts.host += `http${opts.secure === true || isHttpsProtocol() ? 's' : ''}://${opts.endpoint}`;
|
106 | this.opts = opts;
|
107 | }
|
108 |
|
109 | |
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | |
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | public postObject(name: string, file: File | Blob, options: PostObjectOptions={}) {
|
122 | let policyBase64;
|
123 |
|
124 | const data = new FormData();
|
125 |
|
126 | Object.keys(options.headers || {}).forEach(key => {
|
127 | data.append(key, options.headers[key].toString());
|
128 | });
|
129 |
|
130 | const objectKey = objectName((options.dir || '').replace(/^(.+?)\/*$/, '$1/') + objectName(name));
|
131 | data.append('key', objectKey);
|
132 |
|
133 | if (this.opts.accessKeyId && this.opts.accessKeySecret){
|
134 | if (typeof options.policy === 'string'){
|
135 | policyBase64 = options.policy;
|
136 | } else{
|
137 | const policy = {
|
138 | "expiration": new Date(+new Date() + 24 * 3600 * 1000).toISOString(),
|
139 | "conditions": [
|
140 | {"bucket": this.opts.bucket},
|
141 | {"key": objectKey},
|
142 | ["content-length-range", 0, 1024 * 1024 * 1024],
|
143 | ],
|
144 | ...options.policy,
|
145 | };
|
146 | policyBase64 = rstr2b64(utf8Encode(JSON.stringify(policy)));
|
147 | }
|
148 |
|
149 | const signature = options.signature || computeSignature(this.opts.accessKeySecret, policyBase64);
|
150 | data.append('OSSAccessKeyId', this.opts.accessKeyId);
|
151 | data.append('policy', policyBase64);
|
152 | data.append('Signature', signature);
|
153 | if (this.opts.stsToken){
|
154 | data.append('x-oss-security-token', this.opts.stsToken);
|
155 | }
|
156 | }
|
157 |
|
158 | const ossParam = getOssParams(options, ['success_action_status', 'success_action_redirect']);
|
159 | Object.keys(ossParam).forEach(k => data.append(k, ossParam[k]));
|
160 |
|
161 | data.append('file', file);
|
162 |
|
163 | const emptyFunc = function () {};
|
164 | const successCallback = options.onSuccess || emptyFunc;
|
165 | const reqOptions: RequestOptions = {
|
166 | method: 'POST',
|
167 | data,
|
168 | timeout: options.timeout || this.opts.timeout,
|
169 | onSuccess: (result, xhr) => successCallback(result, xhr),
|
170 | onError: options.onError || function (e) {
|
171 | console.error(e);
|
172 | },
|
173 | onAbort: options.onAbort || emptyFunc,
|
174 |
|
175 | };
|
176 | if (options.onProgress) reqOptions.onProgress = options.onProgress;
|
177 | return request(this.opts.host, reqOptions);
|
178 | }
|
179 |
|
180 | |
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 | public generateObjectUrl(name: string, baseUrl?: string) {
|
187 | if (!baseUrl) {
|
188 | baseUrl = this.opts.host + '/';
|
189 | } else if (baseUrl[baseUrl.length - 1] !== '/') {
|
190 | baseUrl += '/';
|
191 | }
|
192 | return baseUrl + escapeName(objectName(name));
|
193 | }
|
194 | }
|
195 |
|
196 |
|
197 | export default Client;
|