leancloud-storage
Version:
LeanCloud JavaScript SDK.
461 lines (385 loc) • 12.6 kB
JavaScript
'use strict';
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of');
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf);
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn');
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2);
var _inherits2 = require('babel-runtime/helpers/inherits');
var _inherits3 = _interopRequireDefault(_inherits2);
var _promise = require('babel-runtime/core-js/promise');
var _promise2 = _interopRequireDefault(_promise);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _getIterator2 = require('babel-runtime/core-js/get-iterator');
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _stringify = require('babel-runtime/core-js/json/stringify');
var _stringify2 = _interopRequireDefault(_stringify);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _require = require('../adapter'),
getAdapter = _require.getAdapter;
var debug = require('debug')('leancloud:qiniu');
var ajax = require('../utils/ajax');
var btoa = require('../utils/btoa');
var SHARD_THRESHOLD = 1024 * 1024 * 64;
var CHUNK_SIZE = 1024 * 1024 * 16;
function upload(uploadInfo, data, file) {
var saveOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
// Get the uptoken to upload files to qiniu.
var uptoken = uploadInfo.token;
var url = uploadInfo.upload_url || 'https://upload.qiniup.com';
var fileFormData = {
field: 'file',
data: data,
name: file.attributes.name
};
var options = {
headers: file._uploadHeaders,
data: {
name: file.attributes.name,
key: uploadInfo.key,
token: uptoken
},
onprogress: saveOptions.onprogress
};
debug('url: %s, file: %o, options: %o', url, fileFormData, options);
var upload = getAdapter('upload');
return upload(url, fileFormData, options).then(function (response) {
debug(response.status, response.data);
if (response.ok === false) {
var message = response.status;
if (response.data) {
if (response.data.error) {
message = response.data.error;
} else {
message = (0, _stringify2.default)(response.data);
}
}
var error = new Error(message);
error.response = response;
throw error;
}
file.attributes.url = uploadInfo.url;
file._bucket = uploadInfo.bucket;
file.id = uploadInfo.objectId;
return file;
}, function (error) {
var response = error.response;
if (response) {
debug(response.status, response.data);
error.statusCode = response.status;
error.response = response.data;
}
throw error;
});
}
function urlSafeBase64(string) {
var base64 = btoa(unescape(encodeURIComponent(string)));
var result = '';
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = (0, _getIterator3.default)(base64), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var ch = _step.value;
switch (ch) {
case '+':
result += '-';
break;
case '/':
result += '_';
break;
default:
result += ch;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return result;
}
var ShardUploader = function () {
function ShardUploader(uploadInfo, data, file, saveOptions) {
var _this = this;
(0, _classCallCheck3.default)(this, ShardUploader);
this.uploadInfo = uploadInfo;
this.data = data;
this.file = file;
this.size = undefined;
this.offset = 0;
this.uploadedChunks = 0;
var key = urlSafeBase64(uploadInfo.key);
var uploadURL = uploadInfo.upload_url || 'https://upload.qiniup.com';
this.baseURL = uploadURL + '/buckets/' + uploadInfo.bucket + '/objects/' + key + '/uploads';
this.upToken = 'UpToken ' + uploadInfo.token;
this.uploaded = 0;
if (saveOptions && saveOptions.onprogress) {
this.onProgress = function (_ref) {
var loaded = _ref.loaded;
loaded += _this.uploadedChunks * CHUNK_SIZE;
if (loaded <= _this.uploaded) {
return;
}
if (_this.size) {
saveOptions.onprogress({
loaded: loaded,
total: _this.size,
percent: loaded / _this.size * 100
});
} else {
saveOptions.onprogress({ loaded: loaded });
}
_this.uploaded = loaded;
};
}
}
/**
* @returns {Promise<string>}
*/
(0, _createClass3.default)(ShardUploader, [{
key: 'getUploadId',
value: function getUploadId() {
return ajax({
method: 'POST',
url: this.baseURL,
headers: {
Authorization: this.upToken
}
}).then(function (res) {
return res.uploadId;
});
}
}, {
key: 'getChunk',
value: function getChunk() {
throw new Error('Not implemented');
}
/**
* @param {string} uploadId
* @param {number} partNumber
* @param {any} data
* @returns {Promise<{ partNumber: number, etag: string }>}
*/
}, {
key: 'uploadPart',
value: function uploadPart(uploadId, partNumber, data) {
return ajax({
method: 'PUT',
url: this.baseURL + '/' + uploadId + '/' + partNumber,
headers: {
Authorization: this.upToken
},
data: data,
onprogress: this.onProgress
}).then(function (_ref2) {
var etag = _ref2.etag;
return { partNumber: partNumber, etag: etag };
});
}
}, {
key: 'stopUpload',
value: function stopUpload(uploadId) {
return ajax({
method: 'DELETE',
url: this.baseURL + '/' + uploadId,
headers: {
Authorization: this.upToken
}
});
}
}, {
key: 'upload',
value: function upload() {
var _this2 = this;
var parts = [];
return this.getUploadId().then(function (uploadId) {
var uploadPart = function uploadPart() {
return _promise2.default.resolve(_this2.getChunk()).then(function (chunk) {
if (!chunk) {
return;
}
var partNumber = parts.length + 1;
return _this2.uploadPart(uploadId, partNumber, chunk).then(function (part) {
parts.push(part);
_this2.uploadedChunks++;
return uploadPart();
});
}).catch(function (error) {
return _this2.stopUpload(uploadId).then(function () {
return _promise2.default.reject(error);
});
});
};
return uploadPart().then(function () {
return ajax({
method: 'POST',
url: _this2.baseURL + '/' + uploadId,
headers: {
Authorization: _this2.upToken
},
data: {
parts: parts,
fname: _this2.file.attributes.name,
mimeType: _this2.file.attributes.mime_type
}
});
});
}).then(function () {
_this2.file.attributes.url = _this2.uploadInfo.url;
_this2.file._bucket = _this2.uploadInfo.bucket;
_this2.file.id = _this2.uploadInfo.objectId;
return _this2.file;
});
}
}]);
return ShardUploader;
}();
var BlobUploader = function (_ShardUploader) {
(0, _inherits3.default)(BlobUploader, _ShardUploader);
function BlobUploader(uploadInfo, data, file, saveOptions) {
(0, _classCallCheck3.default)(this, BlobUploader);
var _this3 = (0, _possibleConstructorReturn3.default)(this, (BlobUploader.__proto__ || (0, _getPrototypeOf2.default)(BlobUploader)).call(this, uploadInfo, data, file, saveOptions));
_this3.size = data.size;
return _this3;
}
/**
* @returns {Blob | null}
*/
(0, _createClass3.default)(BlobUploader, [{
key: 'getChunk',
value: function getChunk() {
if (this.offset >= this.size) {
return null;
}
var chunk = this.data.slice(this.offset, this.offset + CHUNK_SIZE);
this.offset += chunk.size;
return chunk;
}
}]);
return BlobUploader;
}(ShardUploader);
/* NODE-ONLY:start */
var BufferUploader = function (_ShardUploader2) {
(0, _inherits3.default)(BufferUploader, _ShardUploader2);
function BufferUploader(uploadInfo, data, file, saveOptions) {
(0, _classCallCheck3.default)(this, BufferUploader);
var _this4 = (0, _possibleConstructorReturn3.default)(this, (BufferUploader.__proto__ || (0, _getPrototypeOf2.default)(BufferUploader)).call(this, uploadInfo, data, file, saveOptions));
_this4.size = data.length;
return _this4;
}
/**
* @returns {Buffer | null}
*/
(0, _createClass3.default)(BufferUploader, [{
key: 'getChunk',
value: function getChunk() {
if (this.offset >= this.size) {
return null;
}
var chunk = this.data.slice(this.offset, this.offset + CHUNK_SIZE);
this.offset += chunk.length;
return chunk;
}
}]);
return BufferUploader;
}(ShardUploader);
/* NODE-ONLY:end */
/* NODE-ONLY:start */
var StreamUploader = function (_ShardUploader3) {
(0, _inherits3.default)(StreamUploader, _ShardUploader3);
function StreamUploader() {
(0, _classCallCheck3.default)(this, StreamUploader);
return (0, _possibleConstructorReturn3.default)(this, (StreamUploader.__proto__ || (0, _getPrototypeOf2.default)(StreamUploader)).apply(this, arguments));
}
(0, _createClass3.default)(StreamUploader, [{
key: '_read',
/**
* @param {number} [size]
* @returns {Buffer | null}
*/
value: function _read(size) {
var chunk = this.data.read(size);
if (chunk) {
this.offset += chunk.length;
}
return chunk;
}
/**
* @returns {Buffer | null | Promise<Buffer | null>}
*/
}, {
key: 'getChunk',
value: function getChunk() {
var _this6 = this;
if (this.data.readableLength >= CHUNK_SIZE) {
return this._read(CHUNK_SIZE);
}
if (this.data.readableEnded) {
if (this.data.readable) {
return this._read();
}
return null;
}
return new _promise2.default(function (resolve, reject) {
var onReadable = function onReadable() {
var chunk = _this6._read(CHUNK_SIZE);
if (chunk !== null) {
resolve(chunk);
removeListeners();
}
};
var onError = function onError(error) {
reject(error);
removeListeners();
};
var removeListeners = function removeListeners() {
_this6.data.off('readable', onReadable);
_this6.data.off('error', onError);
};
_this6.data.on('readable', onReadable);
_this6.data.on('error', onError);
});
}
}]);
return StreamUploader;
}(ShardUploader);
/* NODE-ONLY:end */
function isBlob(data) {
return typeof Blob !== 'undefined' && data instanceof Blob;
}
/* NODE-ONLY:start */
function isBuffer(data) {
return typeof Buffer !== 'undefined' && Buffer.isBuffer(data);
}
/* NODE-ONLY:end */
/* NODE-ONLY:start */
function isStream(data) {
return typeof require === 'function' && data instanceof require('stream');
}
/* NODE-ONLY:end */
module.exports = function (uploadInfo, data, file) {
var saveOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
if (isBlob(data) && data.size >= SHARD_THRESHOLD) {
return new BlobUploader(uploadInfo, data, file, saveOptions).upload();
}
/* NODE-ONLY:start */
if (isBuffer(data) && data.length >= SHARD_THRESHOLD) {
return new BufferUploader(uploadInfo, data, file, saveOptions).upload();
}
if (isStream(data)) {
return new StreamUploader(uploadInfo, data, file, saveOptions).upload();
}
/* NODE-ONLY:end */
return upload(uploadInfo, data, file, saveOptions);
};