用工小程序前端代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

394 lines
12 KiB

10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
10 months ago
  1. // const debug = require('debug')('ali-oss:object');
  2. const fs = require('fs');
  3. const copy = require('copy-to');
  4. const path = require('path');
  5. const mime = require('mime');
  6. const callback = require('../common/callback');
  7. const merge = require('merge-descriptors');
  8. const { isBlob } = require('../common/utils/isBlob');
  9. const { isFile } = require('../common/utils/isFile');
  10. const { isBuffer } = require('../common/utils/isBuffer');
  11. const { obj2xml } = require('../common/utils/obj2xml');
  12. const { parseRestoreInfo } = require('../common/utils/parseRestoreInfo');
  13. // var assert = require('assert');
  14. const proto = exports;
  15. /**
  16. * Object operations
  17. */
  18. /**
  19. * append an object from String(file path)/Buffer/ReadableStream
  20. * @param {String} name the object key
  21. * @param {Mixed} file String(file path)/Buffer/ReadableStream
  22. * @param {Object} options
  23. * @return {Object}
  24. */
  25. proto.append = async function append(name, file, options) {
  26. options = options || {};
  27. if (options.position === undefined) options.position = '0';
  28. options.subres = {
  29. append: '',
  30. position: options.position
  31. };
  32. options.method = 'POST';
  33. const result = await this.put(name, file, options);
  34. result.nextAppendPosition = result.res.headers['x-oss-next-append-position'];
  35. return result;
  36. };
  37. /**
  38. * put an object from String(file path)/Buffer/ReadableStream
  39. * @param {String} name the object key
  40. * @param {Mixed} file String(file path)/Buffer/ReadableStream
  41. * @param {Object} options
  42. * {Object} [options.callback] The callback parameter is composed of a JSON string encoded in Base64
  43. * {String} options.callback.url the OSS sends a callback request to this URL
  44. * {String} [options.callback.host] The host header value for initiating callback requests
  45. * {String} options.callback.body The value of the request body when a callback is initiated
  46. * {String} [options.callback.contentType] The Content-Type of the callback requests initiated
  47. * {Boolean} [options.callback.callbackSNI] Whether OSS sends SNI to the origin address specified by callbackUrl when a callback request is initiated from the client
  48. * {Object} [options.callback.customValue] Custom parameters are a map of key-values, e.g:
  49. * customValue = {
  50. * key1: 'value1',
  51. * key2: 'value2'
  52. * }
  53. * @return {Object}
  54. */
  55. proto.put = async function put(name, file, options) {
  56. let content;
  57. options = options || {};
  58. options.disabledMD5 = options.disabledMD5 === undefined ? true : !!options.disabledMD5;
  59. options.headers = options.headers || {};
  60. name = this._objectName(name);
  61. if (isBuffer(file)) {
  62. content = file;
  63. } else if (isBlob(file) || isFile(file)) {
  64. if (!options.mime) {
  65. if (isFile(file)) {
  66. options.mime = mime.getType(path.extname(file.name));
  67. } else {
  68. options.mime = file.type;
  69. }
  70. }
  71. content = await this._createBuffer(file, 0, file.size);
  72. options.contentLength = await this._getFileSize(file);
  73. } else {
  74. throw new TypeError('Must provide Buffer/Blob/File for put.');
  75. }
  76. this._convertMetaToHeaders(options.meta, options.headers);
  77. const method = options.method || 'PUT';
  78. const params = this._objectRequestParams(method, name, options);
  79. callback.encodeCallback(params, options);
  80. params.mime = options.mime;
  81. params.disabledMD5 = options.disabledMD5;
  82. params.content = content;
  83. params.successStatuses = [200];
  84. const result = await this.request(params);
  85. const ret = {
  86. name,
  87. url: this._objectUrl(name),
  88. res: result.res
  89. };
  90. if (params.headers && params.headers['x-oss-callback']) {
  91. ret.data = JSON.parse(result.data.toString());
  92. }
  93. return ret;
  94. };
  95. /**
  96. * put an object from ReadableStream. If `options.contentLength` is
  97. * not provided, chunked encoding is used.
  98. * @param {String} name the object key
  99. * @param {Readable} stream the ReadableStream
  100. * @param {Object} options
  101. * @return {Object}
  102. */
  103. proto.putStream = async function putStream(name, stream, options) {
  104. options = options || {};
  105. options.headers = options.headers || {};
  106. name = this._objectName(name);
  107. if (options.contentLength) {
  108. options.headers['Content-Length'] = options.contentLength;
  109. } else {
  110. options.headers['Transfer-Encoding'] = 'chunked';
  111. }
  112. this._convertMetaToHeaders(options.meta, options.headers);
  113. const method = options.method || 'PUT';
  114. const params = this._objectRequestParams(method, name, options);
  115. callback.encodeCallback(params, options);
  116. params.mime = options.mime;
  117. params.stream = stream;
  118. params.successStatuses = [200];
  119. const result = await this.request(params);
  120. const ret = {
  121. name,
  122. url: this._objectUrl(name),
  123. res: result.res
  124. };
  125. if (params.headers && params.headers['x-oss-callback']) {
  126. ret.data = JSON.parse(result.data.toString());
  127. }
  128. return ret;
  129. };
  130. merge(proto, require('../common/object/copyObject'));
  131. merge(proto, require('../common/object/getObjectTagging'));
  132. merge(proto, require('../common/object/putObjectTagging'));
  133. merge(proto, require('../common/object/deleteObjectTagging'));
  134. merge(proto, require('../common/image'));
  135. merge(proto, require('../common/object/getBucketVersions'));
  136. merge(proto, require('../common/object/getACL'));
  137. merge(proto, require('../common/object/putACL'));
  138. merge(proto, require('../common/object/head'));
  139. merge(proto, require('../common/object/delete'));
  140. merge(proto, require('../common/object/get'));
  141. merge(proto, require('../common/object/putSymlink'));
  142. merge(proto, require('../common/object/getSymlink'));
  143. merge(proto, require('../common/object/deleteMulti'));
  144. merge(proto, require('../common/object/getObjectMeta'));
  145. merge(proto, require('../common/object/getObjectUrl'));
  146. merge(proto, require('../common/object/generateObjectUrl'));
  147. merge(proto, require('../common/object/signatureUrl'));
  148. merge(proto, require('../common/object/asyncSignatureUrl'));
  149. merge(proto, require('../common/object/signatureUrlV4'));
  150. merge(proto, require('../common/object/signPostObjectPolicyV4'));
  151. proto.putMeta = async function putMeta(name, meta, options) {
  152. const copyResult = await this.copy(name, name, {
  153. meta: meta || {},
  154. timeout: options && options.timeout,
  155. ctx: options && options.ctx
  156. });
  157. return copyResult;
  158. };
  159. proto.list = async function list(query, options) {
  160. // prefix, marker, max-keys, delimiter
  161. const params = this._objectRequestParams('GET', '', options);
  162. params.query = query;
  163. params.xmlResponse = true;
  164. params.successStatuses = [200];
  165. const result = await this.request(params);
  166. let objects = result.data.Contents || [];
  167. const that = this;
  168. if (objects) {
  169. if (!Array.isArray(objects)) {
  170. objects = [objects];
  171. }
  172. objects = objects.map(obj => ({
  173. name: obj.Key,
  174. url: that._objectUrl(obj.Key),
  175. lastModified: obj.LastModified,
  176. etag: obj.ETag,
  177. type: obj.Type,
  178. size: Number(obj.Size),
  179. storageClass: obj.StorageClass,
  180. owner: {
  181. id: obj.Owner.ID,
  182. displayName: obj.Owner.DisplayName
  183. },
  184. restoreInfo: parseRestoreInfo(obj.RestoreInfo)
  185. }));
  186. }
  187. let prefixes = result.data.CommonPrefixes || null;
  188. if (prefixes) {
  189. if (!Array.isArray(prefixes)) {
  190. prefixes = [prefixes];
  191. }
  192. prefixes = prefixes.map(item => item.Prefix);
  193. }
  194. return {
  195. res: result.res,
  196. objects,
  197. prefixes,
  198. nextMarker: result.data.NextMarker || null,
  199. isTruncated: result.data.IsTruncated === 'true'
  200. };
  201. };
  202. proto.listV2 = async function listV2(query, options = {}) {
  203. const continuation_token = query['continuation-token'] || query.continuationToken;
  204. if (continuation_token) {
  205. options.subres = Object.assign(
  206. {
  207. 'continuation-token': continuation_token
  208. },
  209. options.subres
  210. );
  211. }
  212. const params = this._objectRequestParams('GET', '', options);
  213. params.query = Object.assign({ 'list-type': 2 }, query);
  214. delete params.query['continuation-token'];
  215. delete params.query.continuationToken;
  216. params.xmlResponse = true;
  217. params.successStatuses = [200];
  218. const result = await this.request(params);
  219. let objects = result.data.Contents || [];
  220. const that = this;
  221. if (objects) {
  222. if (!Array.isArray(objects)) {
  223. objects = [objects];
  224. }
  225. objects = objects.map(obj => {
  226. let owner = null;
  227. if (obj.Owner) {
  228. owner = {
  229. id: obj.Owner.ID,
  230. displayName: obj.Owner.DisplayName
  231. };
  232. }
  233. return {
  234. name: obj.Key,
  235. url: that._objectUrl(obj.Key),
  236. lastModified: obj.LastModified,
  237. etag: obj.ETag,
  238. type: obj.Type,
  239. size: Number(obj.Size),
  240. storageClass: obj.StorageClass,
  241. owner,
  242. restoreInfo: parseRestoreInfo(obj.RestoreInfo)
  243. };
  244. });
  245. }
  246. let prefixes = result.data.CommonPrefixes || null;
  247. if (prefixes) {
  248. if (!Array.isArray(prefixes)) {
  249. prefixes = [prefixes];
  250. }
  251. prefixes = prefixes.map(item => item.Prefix);
  252. }
  253. return {
  254. res: result.res,
  255. objects,
  256. prefixes,
  257. isTruncated: result.data.IsTruncated === 'true',
  258. keyCount: +result.data.KeyCount,
  259. continuationToken: result.data.ContinuationToken || null,
  260. nextContinuationToken: result.data.NextContinuationToken || null
  261. };
  262. };
  263. /**
  264. * Restore Object
  265. * @param {String} name the object key
  266. * @param {Object} options
  267. * @returns {{res}}
  268. */
  269. proto.restore = async function restore(name, options = { type: 'Archive' }) {
  270. options = options || {};
  271. options.subres = Object.assign({ restore: '' }, options.subres);
  272. if (options.versionId) {
  273. options.subres.versionId = options.versionId;
  274. }
  275. const params = this._objectRequestParams('POST', name, options);
  276. const paramsXMLObj = {
  277. RestoreRequest: {
  278. Days: options.Days ? options.Days : 2
  279. }
  280. };
  281. if (options.type === 'ColdArchive' || options.type === 'DeepColdArchive') {
  282. paramsXMLObj.RestoreRequest.JobParameters = {
  283. Tier: options.JobParameters ? options.JobParameters : 'Standard'
  284. };
  285. }
  286. params.content = obj2xml(paramsXMLObj, {
  287. headers: true
  288. });
  289. params.mime = 'xml';
  290. params.successStatuses = [202];
  291. const result = await this.request(params);
  292. return {
  293. res: result.res
  294. };
  295. };
  296. proto._objectUrl = function _objectUrl(name) {
  297. return this._getReqUrl({ bucket: this.options.bucket, object: name });
  298. };
  299. /**
  300. * generator request params
  301. * @return {Object} params
  302. *
  303. * @api private
  304. */
  305. proto._objectRequestParams = function _objectRequestParams(method, name, options) {
  306. if (!this.options.bucket && !this.options.cname) {
  307. throw new Error('Please create a bucket first');
  308. }
  309. options = options || {};
  310. name = this._objectName(name);
  311. const params = {
  312. object: name,
  313. bucket: this.options.bucket,
  314. method,
  315. subres: options && options.subres,
  316. additionalHeaders: options && options.additionalHeaders,
  317. timeout: options && options.timeout,
  318. ctx: options && options.ctx
  319. };
  320. if (options.headers) {
  321. params.headers = {};
  322. copy(options.headers).to(params.headers);
  323. }
  324. return params;
  325. };
  326. proto._objectName = function _objectName(name) {
  327. return name.replace(/^\/+/, '');
  328. };
  329. proto._convertMetaToHeaders = function _convertMetaToHeaders(meta, headers) {
  330. if (!meta) {
  331. return;
  332. }
  333. Object.keys(meta).forEach(k => {
  334. headers[`x-oss-meta-${k}`] = meta[k];
  335. });
  336. };
  337. proto._deleteFileSafe = function _deleteFileSafe(filepath) {
  338. return new Promise(resolve => {
  339. fs.exists(filepath, exists => {
  340. if (!exists) {
  341. resolve();
  342. } else {
  343. fs.unlink(filepath, err => {
  344. if (err) {
  345. this.debug('unlink %j error: %s', filepath, err, 'error');
  346. }
  347. resolve();
  348. });
  349. }
  350. });
  351. });
  352. };