| 
						 | 
						- const fs = require('fs');
 - const is = require('is-type-of');
 - const util = require('util');
 - const path = require('path');
 - const mime = require('mime');
 - const { isFile } = require('./common/utils/isFile');
 - const { isArray } = require('./common/utils/isArray');
 - const { isBuffer } = require('./common/utils/isBuffer');
 - const { retry } = require('./common/utils/retry');
 - 
 - const proto = exports;
 - 
 - /**
 -  * Multipart operations
 -  */
 - 
 - /**
 -  * Upload a file to OSS using multipart uploads
 -  * @param {String} name
 -  * @param {String|File|Buffer} file
 -  * @param {Object} options
 -  *        {Object} [options.callback] The callback parameter is composed of a JSON string encoded in Base64
 -  *        {String} options.callback.url the OSS sends a callback request to this URL
 -  *        {String} [options.callback.host] The host header value for initiating callback requests
 -  *        {String} options.callback.body The value of the request body when a callback is initiated
 -  *        {String} [options.callback.contentType] The Content-Type of the callback requests initiated
 -  *        {Boolean} [options.callback.callbackSNI] Whether OSS sends SNI to the origin address specified by callbackUrl when a callback request is initiated from the client
 -  *        {Object} [options.callback.customValue] Custom parameters are a map of key-values, e.g:
 -  *                  customValue = {
 -  *                    key1: 'value1',
 -  *                    key2: 'value2'
 -  *                  }
 -  */
 - proto.multipartUpload = async function multipartUpload(name, file, options) {
 -   this.resetCancelFlag();
 -   options = options || {};
 -   if (options.checkpoint && options.checkpoint.uploadId) {
 -     return await this._resumeMultipart(options.checkpoint, options);
 -   }
 - 
 -   const minPartSize = 100 * 1024;
 -   if (!options.mime) {
 -     if (isFile(file)) {
 -       options.mime = mime.getType(path.extname(file.name));
 -     } else if (isBuffer(file)) {
 -       options.mime = '';
 -     } else {
 -       options.mime = mime.getType(path.extname(file));
 -     }
 -   }
 -   options.headers = options.headers || {};
 -   this._convertMetaToHeaders(options.meta, options.headers);
 - 
 -   const fileSize = await this._getFileSize(file);
 -   if (fileSize < minPartSize) {
 -     options.contentLength = fileSize;
 -     const result = await this.put(name, file, options);
 -     if (options && options.progress) {
 -       await options.progress(1);
 -     }
 - 
 -     const ret = {
 -       res: result.res,
 -       bucket: this.options.bucket,
 -       name,
 -       etag: result.res.headers.etag
 -     };
 - 
 -     if ((options.headers && options.headers['x-oss-callback']) || options.callback) {
 -       ret.data = result.data;
 -     }
 - 
 -     return ret;
 -   }
 - 
 -   if (options.partSize && !(parseInt(options.partSize, 10) === options.partSize)) {
 -     throw new Error('partSize must be int number');
 -   }
 - 
 -   if (options.partSize && options.partSize < minPartSize) {
 -     throw new Error(`partSize must not be smaller than ${minPartSize}`);
 -   }
 - 
 -   const initResult = await this.initMultipartUpload(name, options);
 -   const { uploadId } = initResult;
 -   const partSize = this._getPartSize(fileSize, options.partSize);
 - 
 -   const checkpoint = {
 -     file,
 -     name,
 -     fileSize,
 -     partSize,
 -     uploadId,
 -     doneParts: []
 -   };
 - 
 -   if (options && options.progress) {
 -     await options.progress(0, checkpoint, initResult.res);
 -   }
 - 
 -   return await this._resumeMultipart(checkpoint, options);
 - };
 - 
 - /*
 -  * Resume multipart upload from checkpoint. The checkpoint will be
 -  * updated after each successful part upload.
 -  * @param {Object} checkpoint the checkpoint
 -  * @param {Object} options
 -  */
 - proto._resumeMultipart = async function _resumeMultipart(checkpoint, options) {
 -   const that = this;
 -   if (this.isCancel()) {
 -     throw this._makeCancelEvent();
 -   }
 -   const { file, fileSize, partSize, uploadId, doneParts, name } = checkpoint;
 - 
 -   const partOffs = this._divideParts(fileSize, partSize);
 -   const numParts = partOffs.length;
 -   let uploadPartJob = retry(
 -     (self, partNo) => {
 -       // eslint-disable-next-line no-async-promise-executor
 -       return new Promise(async (resolve, reject) => {
 -         try {
 -           if (!self.isCancel()) {
 -             const pi = partOffs[partNo - 1];
 -             const stream = await self._createStream(file, pi.start, pi.end);
 -             const data = {
 -               stream,
 -               size: pi.end - pi.start
 -             };
 - 
 -             if (isArray(self.multipartUploadStreams)) {
 -               self.multipartUploadStreams.push(data.stream);
 -             } else {
 -               self.multipartUploadStreams = [data.stream];
 -             }
 - 
 -             const removeStreamFromMultipartUploadStreams = function () {
 -               if (!stream.destroyed) {
 -                 stream.destroy();
 -               }
 -               const index = self.multipartUploadStreams.indexOf(stream);
 -               if (index !== -1) {
 -                 self.multipartUploadStreams.splice(index, 1);
 -               }
 -             };
 - 
 -             stream.on('close', removeStreamFromMultipartUploadStreams);
 -             stream.on('error', removeStreamFromMultipartUploadStreams);
 - 
 -             let result;
 -             try {
 -               result = await self._uploadPart(name, uploadId, partNo, data, options);
 -             } catch (error) {
 -               removeStreamFromMultipartUploadStreams();
 -               if (error.status === 404) {
 -                 throw self._makeAbortEvent();
 -               }
 -               throw error;
 -             }
 -             if (!self.isCancel()) {
 -               doneParts.push({
 -                 number: partNo,
 -                 etag: result.res.headers.etag
 -               });
 -               checkpoint.doneParts = doneParts;
 - 
 -               if (options.progress) {
 -                 await options.progress(doneParts.length / (numParts + 1), checkpoint, result.res);
 -               }
 -             }
 -           }
 -           resolve();
 -         } catch (err) {
 -           err.partNum = partNo;
 -           reject(err);
 -         }
 -       });
 -     },
 -     this.options.retryMax,
 -     {
 -       errorHandler: err => {
 -         const _errHandle = _err => {
 -           const statusErr = [-1, -2].includes(_err.status);
 -           const requestErrorRetryHandle = this.options.requestErrorRetryHandle || (() => true);
 -           return statusErr && requestErrorRetryHandle(_err);
 -         };
 -         return !!_errHandle(err);
 -       }
 -     }
 -   );
 - 
 -   const all = Array.from(new Array(numParts), (x, i) => i + 1);
 -   const done = doneParts.map(p => p.number);
 -   const todo = all.filter(p => done.indexOf(p) < 0);
 - 
 -   const defaultParallel = 5;
 -   const parallel = options.parallel || defaultParallel;
 - 
 -   if (this.checkBrowserAndVersion('Internet Explorer', '10') || parallel === 1) {
 -     for (let i = 0; i < todo.length; i++) {
 -       if (this.isCancel()) {
 -         throw this._makeCancelEvent();
 -       }
 -       /* eslint no-await-in-loop: [0] */
 -       await uploadPartJob(this, todo[i]);
 -     }
 -   } else {
 -     // upload in parallel
 -     const jobErr = await this._parallel(todo, parallel, value => {
 -       return new Promise((resolve, reject) => {
 -         uploadPartJob(that, value)
 -           .then(() => {
 -             resolve();
 -           })
 -           .catch(reject);
 -       });
 -     });
 - 
 -     const abortEvent = jobErr.find(err => err.name === 'abort');
 -     if (abortEvent) throw abortEvent;
 - 
 -     if (this.isCancel()) {
 -       uploadPartJob = null;
 -       throw this._makeCancelEvent();
 -     }
 - 
 -     if (jobErr && jobErr.length > 0) {
 -       jobErr[0].message = `Failed to upload some parts with error: ${jobErr[0].toString()} part_num: ${
 -         jobErr[0].partNum
 -       }`;
 -       throw jobErr[0];
 -     }
 -   }
 - 
 -   return await this.completeMultipartUpload(name, uploadId, doneParts, options);
 - };
 - 
 - /**
 -  * Get file size
 -  */
 - proto._getFileSize = async function _getFileSize(file) {
 -   if (isBuffer(file)) {
 -     return file.length;
 -   } else if (isFile(file)) {
 -     return file.size;
 -   } else if (is.string(file)) {
 -     const stat = await this._statFile(file);
 -     return stat.size;
 -   }
 - 
 -   throw new Error('_getFileSize requires Buffer/File/String.');
 - };
 - 
 - /*
 -  * Readable stream for Web File
 -  */
 - const { Readable } = require('stream');
 - 
 - function WebFileReadStream(file, options) {
 -   if (!(this instanceof WebFileReadStream)) {
 -     return new WebFileReadStream(file, options);
 -   }
 - 
 -   Readable.call(this, options);
 - 
 -   this.file = file;
 -   this.reader = new FileReader();
 -   this.start = 0;
 -   this.finish = false;
 -   this.fileBuffer = null;
 - }
 - util.inherits(WebFileReadStream, Readable);
 - 
 - WebFileReadStream.prototype.readFileAndPush = function readFileAndPush(size) {
 -   if (this.fileBuffer) {
 -     let pushRet = true;
 -     while (pushRet && this.fileBuffer && this.start < this.fileBuffer.length) {
 -       const { start } = this;
 -       let end = start + size;
 -       end = end > this.fileBuffer.length ? this.fileBuffer.length : end;
 -       this.start = end;
 -       pushRet = this.push(this.fileBuffer.slice(start, end));
 -     }
 -   }
 - };
 - 
 - WebFileReadStream.prototype._read = function _read(size) {
 -   if (
 -     (this.file && this.start >= this.file.size) ||
 -     (this.fileBuffer && this.start >= this.fileBuffer.length) ||
 -     this.finish ||
 -     (this.start === 0 && !this.file)
 -   ) {
 -     if (!this.finish) {
 -       this.fileBuffer = null;
 -       this.finish = true;
 -     }
 -     this.push(null);
 -     return;
 -   }
 - 
 -   const defaultReadSize = 16 * 1024;
 -   size = size || defaultReadSize;
 - 
 -   const that = this;
 -   this.reader.onload = function (e) {
 -     that.fileBuffer = Buffer.from(new Uint8Array(e.target.result));
 -     that.file = null;
 -     that.readFileAndPush(size);
 -   };
 -   this.reader.onerror = function onload(e) {
 -     const error = e.srcElement && e.srcElement.error;
 -     if (error) {
 -       throw error;
 -     }
 -     throw e;
 -   };
 - 
 -   if (this.start === 0) {
 -     this.reader.readAsArrayBuffer(this.file);
 -   } else {
 -     this.readFileAndPush(size);
 -   }
 - };
 - 
 - proto._createStream = function _createStream(file, start, end) {
 -   if (is.readableStream(file)) {
 -     return file;
 -   } else if (isFile(file)) {
 -     return new WebFileReadStream(file.slice(start, end));
 -   } else if (isBuffer(file)) {
 -     const iterable = file.subarray(start, end);
 -     // we can't use Readable.from() since it is only support in Node v10
 -     return new Readable({
 -       read() {
 -         this.push(iterable);
 -         this.push(null);
 -       }
 -     });
 -   } else if (is.string(file)) {
 -     return fs.createReadStream(file, {
 -       start,
 -       end: end - 1
 -     });
 -   }
 -   throw new Error('_createStream requires Buffer/File/String.');
 - };
 - 
 - proto._getPartSize = function _getPartSize(fileSize, partSize) {
 -   const maxNumParts = 10 * 1000;
 -   const defaultPartSize = 1 * 1024 * 1024;
 - 
 -   if (!partSize) partSize = defaultPartSize;
 -   const safeSize = Math.ceil(fileSize / maxNumParts);
 - 
 -   if (partSize < safeSize) {
 -     partSize = safeSize;
 -     console.warn(
 -       `partSize has been set to ${partSize}, because the partSize you provided causes partNumber to be greater than 10,000`
 -     );
 -   }
 -   return partSize;
 - };
 - 
 - proto._divideParts = function _divideParts(fileSize, partSize) {
 -   const numParts = Math.ceil(fileSize / partSize);
 - 
 -   const partOffs = [];
 -   for (let i = 0; i < numParts; i++) {
 -     const start = partSize * i;
 -     const end = Math.min(start + partSize, fileSize);
 - 
 -     partOffs.push({
 -       start,
 -       end
 -     });
 -   }
 - 
 -   return partOffs;
 - };
 
 
  |