|
@@ -0,0 +1,346 @@
|
|
|
|
+var util = require("thinkjs-util");
|
|
|
|
+var request = require("request");
|
|
|
|
+var fs = require("fs");
|
|
|
|
+var path = require("path");
|
|
|
|
+
|
|
|
|
+module.exports = util.Class(function () {
|
|
|
|
+ return {
|
|
|
|
+ /**
|
|
|
|
+ * 接口的域名
|
|
|
|
+ * @type {String}
|
|
|
|
+ */
|
|
|
|
+ domain: "v0.api.upyun.com",
|
|
|
|
+ /**
|
|
|
|
+ * 初始化
|
|
|
|
+ * @param {[type]} bucketname [description]
|
|
|
|
+ * @param {[type]} username [description]
|
|
|
|
+ * @param {[type]} password [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ init: function (bucketname, username, password) {
|
|
|
|
+ this.bucketname = bucketname;
|
|
|
|
+ this.username = username;
|
|
|
|
+ this.password = util.md5(password);
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 设置接口的domain
|
|
|
|
+ * @param {[type]} domain [description]
|
|
|
|
+ */
|
|
|
|
+ setDomain: function (domain) {
|
|
|
|
+ this.domain = domain;
|
|
|
|
+ return this;
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 签名
|
|
|
|
+ * @param {[type]} method [description]
|
|
|
|
+ * @param {[type]} uri [description]
|
|
|
|
+ * @param {[type]} date [description]
|
|
|
|
+ * @param {[type]} length [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ sign: function (method, uri, date, length) {
|
|
|
|
+ var sign = method + '&' + uri + '&' + date + '&' + length + '&' + this.password;
|
|
|
|
+ return 'UpYun ' + this.username + ':' + util.md5(sign);
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 获取文件或者文件夹的信息
|
|
|
|
+ * @param {[type]} file [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ getInfo: function (file) {
|
|
|
|
+ return this.request(file, 'HEAD').then(function (response) {
|
|
|
|
+ var headers = response.headers;
|
|
|
|
+ return {
|
|
|
|
+ type: headers['x-upyun-file-type'],
|
|
|
|
+ size: headers['x-upyun-file-size'],
|
|
|
|
+ date: headers['x-upyun-file-date']
|
|
|
|
+ };
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 查看空间占用信息
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ getUsage: function (path) {
|
|
|
|
+ path = path || "/";
|
|
|
|
+ return this.request(path + "?usage").then(function (response) {
|
|
|
|
+ return parseInt(response.body, 10);
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 从返回的headers里获取图片的信息
|
|
|
|
+ * @param {[type]} response [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ getPicInfo: function (response) {
|
|
|
|
+ var headers = response.headers;
|
|
|
|
+ return {
|
|
|
|
+ width: headers['x-upyun-width'],
|
|
|
|
+ height: headers['x-upyun-height'],
|
|
|
|
+ frames: headers['x-upyun-frames'],
|
|
|
|
+ type: headers['x-upyun-file-type']
|
|
|
|
+ };
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 上传文件或者文件夹
|
|
|
|
+ * @param {[type]} savePath [description]
|
|
|
|
+ * @param {[type]} filePath [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ upload: function (savePath, filePath, headers) {
|
|
|
|
+ var defaultHeaders = {
|
|
|
|
+ mkdir: true
|
|
|
|
+ };
|
|
|
|
+ if (util.isObject(headers)) {
|
|
|
|
+ defaultHeaders = util.extend(defaultHeaders, headers);
|
|
|
|
+ } else if (headers) {
|
|
|
|
+ defaultHeaders["Content-Secret"] = headers;
|
|
|
|
+ }
|
|
|
|
+ var self = this;
|
|
|
|
+ //文件上传
|
|
|
|
+ if (util.isFile(filePath)) {
|
|
|
|
+ var stream = fs.readFileSync(filePath);
|
|
|
|
+ var filename;
|
|
|
|
+ if (!(/\.\w+$/.test(savePath))) {
|
|
|
|
+ filename = filePath.split('/');
|
|
|
|
+ filename = filename[filename.length - 1];
|
|
|
|
+ savePath += '/' + filename;
|
|
|
|
+ }
|
|
|
|
+ return this.request(savePath, 'PUT', stream, defaultHeaders).then(function (response) {
|
|
|
|
+ return self.getPicInfo(response);
|
|
|
|
+ }).then(function (data) {
|
|
|
|
+ if (filename) {
|
|
|
|
+ data.filename = filename;
|
|
|
|
+ }
|
|
|
|
+ return data;
|
|
|
|
+ });
|
|
|
|
+ } else if (util.isDir(filePath)) { //文件夹上传
|
|
|
|
+ if (savePath.slice(-1) !== '/') {
|
|
|
|
+ savePath += '/';
|
|
|
|
+ }
|
|
|
|
+ if (filePath.slice(-1) !== '/') {
|
|
|
|
+ filePath += '/';
|
|
|
|
+ }
|
|
|
|
+ var promises = [];
|
|
|
|
+ var files = fs.readdirSync(filePath);
|
|
|
|
+ files.forEach(function (item) {
|
|
|
|
+ var nFilePath = filePath + item;
|
|
|
|
+ var state = fs.statSync(nFilePath);
|
|
|
|
+ if (state.isFile() || state.isDirectory()) {
|
|
|
|
+ var promise = self.upload(savePath + item, nFilePath);
|
|
|
|
+ promises.push(promise);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ if (promises.length) {
|
|
|
|
+ return util.Promise.all(promises);
|
|
|
|
+ } else {
|
|
|
|
+ return self.mkDir(savePath);
|
|
|
|
+ }
|
|
|
|
+ } else { //普通内容上传
|
|
|
|
+ return this.request(savePath, 'PUT', filePath, defaultHeaders).then(function (response) {
|
|
|
|
+ return self.getPicInfo(response);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 文件或者文件夹下载
|
|
|
|
+ * @param {[type]} path [description]
|
|
|
|
+ * @param {[type]} savePath [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ download: function (sourcePath, savePath, typeData) {
|
|
|
|
+ sourcePath = sourcePath || "/";
|
|
|
|
+ //if (savePath && savePath.slice(-1) !== "/") {
|
|
|
|
+ // savePath += "/";
|
|
|
|
+ //}
|
|
|
|
+ var self = this;
|
|
|
|
+ var promise = typeData ? util.getPromise(typeData) : this.getInfo(sourcePath);
|
|
|
|
+ return promise.then(function (data) {
|
|
|
|
+ if (data.type === 'folder') {
|
|
|
|
+ if (sourcePath.slice(-1) !== "/") {
|
|
|
|
+ sourcePath += "/";
|
|
|
|
+ }
|
|
|
|
+ return self.readDir(sourcePath).then(function (data) {
|
|
|
|
+ var promises = [];
|
|
|
|
+ data.forEach(function (item) {
|
|
|
|
+ var nPath = sourcePath + item.name;
|
|
|
|
+ var promise;
|
|
|
|
+ //文件夹
|
|
|
|
+ if (item.type === 'F') {
|
|
|
|
+ promise = self.download(nPath + "/", savePath + item.name + "/", {
|
|
|
|
+ type: 'folder'
|
|
|
|
+ });
|
|
|
|
+ } else if (item.type) { //文件
|
|
|
|
+ promise = self.download(nPath, savePath, {
|
|
|
|
+ type: 'file'
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ promises.push(promise);
|
|
|
|
+ });
|
|
|
|
+ return util.Promise.all(promises);
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ //单个文件
|
|
|
|
+ return self.request(sourcePath, 'GET', '', {}, {
|
|
|
|
+ encoding: null
|
|
|
|
+ }).then(function (response) {
|
|
|
|
+ if (!savePath) {
|
|
|
|
+ return response.body;
|
|
|
|
+ }
|
|
|
|
+ var sourceExt = path.extname(sourcePath);
|
|
|
|
+ var saveExt = path.extname(savePath);
|
|
|
|
+ var fileSavePath = savePath;
|
|
|
|
+ if (sourceExt && sourceExt === saveExt) {
|
|
|
|
+ util.mkdir(path.dirname(savePath));
|
|
|
|
+ } else {
|
|
|
|
+ util.mkdir(savePath);
|
|
|
|
+ fileSavePath = savePath + path.basename(sourcePath);
|
|
|
|
+ }
|
|
|
|
+ fs.writeFileSync(fileSavePath, response.body);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 删除文件或者文件夹
|
|
|
|
+ * @param {[type]} path [description]
|
|
|
|
+ * @param {[type]} force [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ rm: function (path, force) {
|
|
|
|
+ if (!path) {
|
|
|
|
+ return util.getPromise(new Error("path can't empty"), true);
|
|
|
|
+ }
|
|
|
|
+ if (path.slice(-1) !== '/') {
|
|
|
|
+ path += '/';
|
|
|
|
+ }
|
|
|
|
+ var self = this;
|
|
|
|
+ return this.getInfo(path).then(function (data) {
|
|
|
|
+ if (data.type === 'folder') {
|
|
|
|
+ if (!force) {
|
|
|
|
+ return self.request(path, 'DELETE').then(function (response) {
|
|
|
|
+ return response.body;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ return self.readDir(path).then(function (data) {
|
|
|
|
+ var promises = [];
|
|
|
|
+ data.forEach(function (item) {
|
|
|
|
+ var nPath = path + item.name;
|
|
|
|
+ var promise;
|
|
|
|
+ //文件夹
|
|
|
|
+ if (item.type === 'F') {
|
|
|
|
+ promise = self.rm(nPath + "/", true);
|
|
|
|
+ } else if (item.type) { //文件
|
|
|
|
+ promise = self.rm(nPath);
|
|
|
|
+ }
|
|
|
|
+ promises.push(promise);
|
|
|
|
+ });
|
|
|
|
+ if (promises.length) {
|
|
|
|
+ return util.Promise.all(promises);
|
|
|
|
+ }
|
|
|
|
+ }).then(function () {
|
|
|
|
+ return self.rm(path, false);
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ return self.request(path, 'DELETE').then(function (response) {
|
|
|
|
+ return response.body;
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 递归创建目录
|
|
|
|
+ * @param {[type]} path [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ mkDir: function (path) {
|
|
|
|
+ return this.request(path, 'PUT', '', {
|
|
|
|
+ mkdir: true,
|
|
|
|
+ folder: true
|
|
|
|
+ }).then(function (response) {
|
|
|
|
+ return response.body;
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 读取目录下的文件和子目录
|
|
|
|
+ * @param {[type]} dir [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ readDir: function (path, recursive) {
|
|
|
|
+ path = path || "/";
|
|
|
|
+ if (path.slice(-1) !== '/') {
|
|
|
|
+ path += '/';
|
|
|
|
+ }
|
|
|
|
+ var self = this;
|
|
|
|
+ return this.request(path, "GET").then(function (response) {
|
|
|
|
+ var dirs = response.body.split("\n");
|
|
|
|
+ var result = [];
|
|
|
|
+ var promises = [];
|
|
|
|
+ for (var i = 0; i < dirs.length; i++) {
|
|
|
|
+ var dir = dirs[i];
|
|
|
|
+ var attrs = dir.split("\t");
|
|
|
|
+ dir = {
|
|
|
|
+ name: attrs[0],
|
|
|
|
+ type: attrs[1],
|
|
|
|
+ size: attrs[2],
|
|
|
|
+ time: attrs[3]
|
|
|
|
+ };
|
|
|
|
+ if (recursive && dir.type === 'F') {
|
|
|
|
+ var promise = self.readDir(path + dir.name, true).then(function (data) {
|
|
|
|
+ dir.children = data;
|
|
|
|
+ });
|
|
|
|
+ promises.push(promise);
|
|
|
|
+ }
|
|
|
|
+ result.push(dir);
|
|
|
|
+ }
|
|
|
|
+ if (promises.length) {
|
|
|
|
+ return util.Promise.all(promises).then(function () {
|
|
|
|
+ return result;
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ return result;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ },
|
|
|
|
+ /**
|
|
|
|
+ * 请求数据
|
|
|
|
+ * @param {[type]} uri [description]
|
|
|
|
+ * @param {[type]} method [description]
|
|
|
|
+ * @param {[type]} data [description]
|
|
|
|
+ * @param {[type]} headers [description]
|
|
|
|
+ * @param {[type]} options [description]
|
|
|
|
+ * @return {[type]} [description]
|
|
|
|
+ */
|
|
|
|
+ request: function (uri, method, data, headers, options) {
|
|
|
|
+ uri = "/" + this.bucketname + uri;
|
|
|
|
+ method = method || "GET";
|
|
|
|
+ headers = headers || {};
|
|
|
|
+ var length = 0;
|
|
|
|
+ if (data) {
|
|
|
|
+ length = !util.isBuffer(data) ? Buffer.byteLength(data) : data.length;
|
|
|
|
+ }
|
|
|
|
+ var date = (new Date()).toUTCString();
|
|
|
|
+ var Authorization = this.sign(method, uri, date, length);
|
|
|
|
+ headers = util.extend(headers, {
|
|
|
|
+ 'Content-Length': length,
|
|
|
|
+ 'Date': date,
|
|
|
|
+ 'Authorization': Authorization
|
|
|
|
+ });
|
|
|
|
+ var deferred = util.getDefer();
|
|
|
|
+ var opts = util.extend({
|
|
|
|
+ url: "http://" + this.domain + uri,
|
|
|
|
+ method: method,
|
|
|
|
+ body: data || "",
|
|
|
|
+ headers: headers
|
|
|
|
+ }, options);
|
|
|
|
+ request(opts, function (error, response, body) {
|
|
|
|
+ if (error || response.statusCode !== 200) {
|
|
|
|
+ deferred.reject(error || "statusCode: " + response.statusCode + "; body: " + body);
|
|
|
|
+ } else {
|
|
|
|
+ deferred.resolve(response);
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ return deferred.promise;
|
|
|
|
+ }
|
|
|
|
+ };
|
|
|
|
+});
|