UNPKG

6.39 kBPlain TextView Raw
1import {escapeName, isHttpsProtocol, objectName} from "./utils";
2import {rstr2b64, utf8Encode} from "./utils/hash/utils";
3import request, {RequestOptions, RequestResult, UploadProgressEvent} from "./utils/request";
4import {computeSignature} from "./utils/signUtils";
5
6function 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// @link https://help.aliyun.com/document_detail/31837.html?spm=a2c4g.11186623.2.15.169769cbIq4eNn#concept-zt4-cvy-5db
18type 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
23export interface ClientOptions {
24 /**
25 * access key you create on aliyun console website
26 */
27 accessKeyId: string;
28 /**
29 * access secret you create
30 */
31 accessKeySecret: string;
32 bucket?: string;
33 /**
34 * used by temporary authorization
35 */
36 stsToken?: string;
37 /**
38 * oss region domain. It takes priority over `region`
39 */
40 endpoint?: string;
41 /**
42 * the bucket data region location, default is `oss-cn-hangzhou`
43 */
44 region?: Region;
45 /**
46 * access OSS with aliyun internal network or not, default is false. If your servers are running on aliyun too,
47 * you can set true to save lot of money.
48 */
49 internal?: boolean;
50 /**
51 * instance level timeout for all operations, default is 60_000(60s).
52 */
53 timeout?: number;
54 secure?: boolean;
55 /**
56 * default false, access oss with custom domain name. if true, you can fill endpoint field with your custom domain name
57 */
58 cname?: boolean;
59 // isRequestPay?: boolean;
60}
61
62export 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
78class 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 // cname: false,
91 secure: undefined,
92 ...options,
93 host: '',
94 };
95 if (opts.endpoint) {
96 // if set custom endpoint
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 // 一般情况下不需要设置 `secure` 参数,唯一需要用到的场景可能就是在 http 页面中使用 https 连接了
105 opts.host += `http${opts.secure === true || isHttpsProtocol() ? 's' : ''}://${opts.endpoint}`;
106 this.opts = opts;
107 }
108
109 /*
110 public putObject(name, file: File|BlobPart, options){
111
112 }
113 */
114
115 /**
116 * post object as form/multi-part
117 * @param name
118 * @param file
119 * @param options
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}, // equals to ["eq", "$key", objectName(name)],
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 // withCredentials: true,
175 };
176 if (options.onProgress) reqOptions.onProgress = options.onProgress;
177 return request(this.opts.host, reqOptions);
178 }
179
180 /**
181 * Get the Object url.
182 * If provide baseUrl, will use baseUrl instead the default bucket and endpoint .
183 * @param name
184 * @param baseUrl
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
197export default Client;