用工小程序前端代码
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.

386 lines
11 KiB

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