kopia lustrzana https://github.com/c9/core
Support for downloading workspace files as a zip archive
rodzic
f1b826172d
commit
bd305b227c
|
@ -2,7 +2,7 @@ define(function(require, exports, module) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
main.consumes = [
|
main.consumes = [
|
||||||
"Plugin", "c9", "ui", "menus", "tree", "info", "vfs"
|
"Plugin", "c9", "ui", "menus", "tree", "info", "vfs", "preferences", "settings"
|
||||||
];
|
];
|
||||||
main.provides = ["download"];
|
main.provides = ["download"];
|
||||||
return main;
|
return main;
|
||||||
|
@ -15,7 +15,12 @@ define(function(require, exports, module) {
|
||||||
var tree = imports.tree;
|
var tree = imports.tree;
|
||||||
var vfs = imports.vfs;
|
var vfs = imports.vfs;
|
||||||
var info = imports.info;
|
var info = imports.info;
|
||||||
|
var prefs = imports.preferences;
|
||||||
|
var settings = imports.settings;
|
||||||
|
|
||||||
|
var SETTING_NAME = "downloadFilesAs";
|
||||||
|
var SETTING_PATH = "user/general/@" + SETTING_NAME;
|
||||||
|
|
||||||
/***** Initialization *****/
|
/***** Initialization *****/
|
||||||
|
|
||||||
var plugin = new Plugin("Ajax.org", main.consumes);
|
var plugin = new Plugin("Ajax.org", main.consumes);
|
||||||
|
@ -40,6 +45,28 @@ define(function(require, exports, module) {
|
||||||
onclick: download
|
onclick: download
|
||||||
}), 140, plugin);
|
}), 140, plugin);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Preferences
|
||||||
|
prefs.add({
|
||||||
|
"General" : {
|
||||||
|
"Tree & Navigate" : {
|
||||||
|
"Download Files As" : {
|
||||||
|
type: "dropdown",
|
||||||
|
path: SETTING_PATH,
|
||||||
|
items: [
|
||||||
|
{ caption : "auto", value : "auto" },
|
||||||
|
{ caption : "tar.gz", value : "tar.gz" },
|
||||||
|
{ caption : "zip", value : "zip" }
|
||||||
|
],
|
||||||
|
position: 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, plugin);
|
||||||
|
|
||||||
|
settings.on("read", function() {
|
||||||
|
settings.setDefaults("user/general", [[SETTING_NAME, "auto"]]);
|
||||||
|
}, plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
function download() {
|
function download() {
|
||||||
|
@ -55,25 +82,40 @@ define(function(require, exports, module) {
|
||||||
if (node.isFolder && node.path == "/")
|
if (node.isFolder && node.path == "/")
|
||||||
downloadProject();
|
downloadProject();
|
||||||
else if (paths.length > 1)
|
else if (paths.length > 1)
|
||||||
vfs.download(paths);
|
downloadPaths(paths);
|
||||||
else if (node.isFolder)
|
else if (node.isFolder)
|
||||||
downloadFolder(node.path);
|
downloadFolder(node.path);
|
||||||
else
|
else
|
||||||
downloadFile(node.path);
|
downloadFile(node.path);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadProject() {
|
function downloadProject() {
|
||||||
vfs.download("/", info.getWorkspace().name + ".tar.gz");
|
vfs.download("/", info.getWorkspace().name + getArchiveFileExtension());
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadPaths(paths) {
|
||||||
|
vfs.download(paths, info.getWorkspace().name + getArchiveFileExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadFolder(path) {
|
function downloadFolder(path) {
|
||||||
vfs.download(path.replace(/\/*$/, "/"));
|
var withTrailingSlash = path.replace(/\/*$/, "/");
|
||||||
|
var parts = withTrailingSlash.split("/");
|
||||||
|
var lastPart = parts[parts.length - 2];
|
||||||
|
vfs.download(withTrailingSlash, lastPart + getArchiveFileExtension());
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadFile(path) {
|
function downloadFile(path) {
|
||||||
vfs.download(path.replace(/\/*$/, ""), null, true);
|
vfs.download(path.replace(/\/*$/, ""), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getArchiveFileExtension() {
|
||||||
|
var downloadFilesAs = settings.get(SETTING_PATH);
|
||||||
|
if (downloadFilesAs === 'auto' || !downloadFilesAs) {
|
||||||
|
downloadFilesAs = /Win/.test(navigator.platform) ? 'zip' : 'tar.gz';
|
||||||
|
}
|
||||||
|
return '.' + downloadFilesAs;
|
||||||
|
}
|
||||||
|
|
||||||
/***** Lifecycle *****/
|
/***** Lifecycle *****/
|
||||||
|
|
||||||
|
|
|
@ -80,41 +80,62 @@ define(function(require, exports, module) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO add support for downloding as zip on windows
|
var executable, args, contentType;
|
||||||
// var cwd;
|
|
||||||
// var args = ["-r", "-"];
|
if (/\.zip$/.test(filename)) {
|
||||||
// paths.forEach(function(path) {
|
executable = "zip";
|
||||||
// if (!path) return;
|
args = ["-r", "-", "--"];
|
||||||
// var dir = Path.dirname(path);
|
contentType = "application/zip"
|
||||||
// if (!cwd) cwd = dir;
|
}
|
||||||
// var name = Path.relative(cwd, path);
|
else {
|
||||||
// if (name[0] == "-") name = "./" + name;
|
executable = "tar";
|
||||||
// args.push(name);
|
args = ["-zcf", "-", "--"];
|
||||||
// });
|
contentType = "application/x-gzip"
|
||||||
// vfs.spawn("zip", { args: args, cwd: cwd }
|
}
|
||||||
|
|
||||||
var args = ["-zcf", "-"];
|
// Find the longest common parent directory of all the paths.
|
||||||
paths.forEach(function(path) {
|
var cwd = null;
|
||||||
|
paths.forEach(function (path) {
|
||||||
if (!path) return;
|
if (!path) return;
|
||||||
var dir = Path.dirname(path);
|
var dir = Path.dirname(path);
|
||||||
var name = Path.basename(path);
|
if (!cwd) {
|
||||||
if (name[0] == "-")
|
cwd = dir;
|
||||||
name = "--add-file=" + name;
|
}
|
||||||
args.push("-C" + dir, name);
|
else {
|
||||||
|
var relative = Path.relative(cwd, dir).split(Path.sep);
|
||||||
|
var i = 0;
|
||||||
|
while (relative[i] === '..') {
|
||||||
|
cwd = Path.resolve(cwd, '..');
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
vfs.spawn("tar", { args: args }, function (err, meta) {
|
paths.forEach(function(path) {
|
||||||
|
if (!path) return;
|
||||||
|
path = Path.relative(cwd, path);
|
||||||
|
// tar misinterprets the Windows path separator as an escape sequence, so use forward slash.
|
||||||
|
if (Path.sep === '\\') {
|
||||||
|
path = path.replace(/\\/g, '/');
|
||||||
|
}
|
||||||
|
args.push(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
vfs.spawn(executable, {
|
||||||
|
args: args,
|
||||||
|
cwd: cwd
|
||||||
|
}, function (err, meta) {
|
||||||
if (err)
|
if (err)
|
||||||
return next(err);
|
return next(err);
|
||||||
|
|
||||||
process = meta.process;
|
process = meta.process;
|
||||||
|
|
||||||
// once we receive data on stdout pipe it to the response
|
// once we receive data on stdout pipe it to the response
|
||||||
process.stdout.once("data", function (data) {
|
process.stdout.once("data", function (data) {
|
||||||
if (res.headerSent)
|
if (res.headerSent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
res.writeHead(200, {
|
res.writeHead(200, {
|
||||||
"Content-Type": "application/x-gzip",
|
"Content-Type": contentType,
|
||||||
"Content-Disposition": filenameHeader
|
"Content-Disposition": filenameHeader
|
||||||
});
|
});
|
||||||
res.write(data);
|
res.write(data);
|
||||||
|
@ -125,7 +146,7 @@ define(function(require, exports, module) {
|
||||||
process.stderr.on("data", function (data) {
|
process.stderr.on("data", function (data) {
|
||||||
stderr += data;
|
stderr += data;
|
||||||
});
|
});
|
||||||
|
|
||||||
process.on("exit", function(code, signal) {
|
process.on("exit", function(code, signal) {
|
||||||
if (res.headerSent)
|
if (res.headerSent)
|
||||||
return;
|
return;
|
||||||
|
@ -133,16 +154,16 @@ define(function(require, exports, module) {
|
||||||
var err;
|
var err;
|
||||||
if (code == 127) {
|
if (code == 127) {
|
||||||
err = new error.PreconditionFailed(
|
err = new error.PreconditionFailed(
|
||||||
"Your instance seems to be missing the 'tar' utility\n" +
|
"Your instance seems to be missing the '" + executable + "' utility\n" +
|
||||||
"If you are using an SSH workspace, please do:\n" +
|
"If you are using an SSH workspace, please do:\n" +
|
||||||
" 'sudo apt-get install tar'");
|
" 'sudo apt-get install " + executable + "'");
|
||||||
} else if (code) {
|
} else if (code) {
|
||||||
err = new error.InternalServerError(
|
err = new error.InternalServerError(
|
||||||
"'tar' utility failed with exit code " + code +
|
"'" + executable + "' utility failed with exit code " + code +
|
||||||
" and stderr:/n'" + stderr + "'");
|
" and stderr:/n'" + stderr + "'");
|
||||||
} else if (signal) {
|
} else if (signal) {
|
||||||
err = new error.InternalServerError(
|
err = new error.InternalServerError(
|
||||||
"'tar' utility was terminated by signal " + signal
|
"'" + executable + "' utility was terminated by signal " + signal
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,8 +53,9 @@ describe(__filename, function(){
|
||||||
|
|
||||||
describe("download", function() {
|
describe("download", function() {
|
||||||
|
|
||||||
it("should download", function(next) {
|
it("should download as tar", function(next) {
|
||||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
var filename = path + "/download.tar.gz";
|
var filename = path + "/download.tar.gz";
|
||||||
var file = fs.createWriteStream(filename);
|
var file = fs.createWriteStream(filename);
|
||||||
http.get("http://localhost:8787/?download=download.tar.gz", function(res) {
|
http.get("http://localhost:8787/?download=download.tar.gz", function(res) {
|
||||||
|
@ -77,21 +78,22 @@ describe(__filename, function(){
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should download sub directory", function(next) {
|
it("should download sub directory as tar", function(next) {
|
||||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
assert.equal(err, null);
|
assert.equal(err, null);
|
||||||
|
|
||||||
var filename = path + "/download.tar.gz";
|
var filename = path + "/download.tar.gz";
|
||||||
var file = fs.createWriteStream(filename);
|
var file = fs.createWriteStream(filename);
|
||||||
http.get("http://localhost:8787/views?download=download.tar.gz", function(res) {
|
http.get("http://localhost:8787/test?download=download.tar.gz", function(res) {
|
||||||
res.pipe(file);
|
res.pipe(file);
|
||||||
|
|
||||||
res.on("end", function() {
|
res.on("end", function() {
|
||||||
execFile("tar", ["-zxvf", filename, "views/status.html.ejs"], {cwd: path}, function(err) {
|
execFile("tar", ["-zxvf", filename, "test/dir1/testdata1.txt"], {cwd: path}, function(err) {
|
||||||
assert.equal(err, null);
|
assert.equal(err, null);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
fs.readFileSync(__dirname + "/views/status.html.ejs", "utf8"),
|
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||||
fs.readFileSync(path + "/views/status.html.ejs", "utf8")
|
fs.readFileSync(path + "/test/dir1/testdata1.txt", "utf8")
|
||||||
);
|
);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -102,22 +104,180 @@ describe(__filename, function(){
|
||||||
|
|
||||||
it("should download without specifying a name", function(next) {
|
it("should download without specifying a name", function(next) {
|
||||||
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
assert.equal(err, null);
|
assert.equal(err, null);
|
||||||
|
|
||||||
var filename = path + "/download.tar.gz";
|
var filename = path + "/download.tar.gz";
|
||||||
var file = fs.createWriteStream(filename);
|
var file = fs.createWriteStream(filename);
|
||||||
http.get("http://localhost:8787/views?download", function(res) {
|
http.get("http://localhost:8787/test?download", function(res) {
|
||||||
assert.equal(res.headers["content-type"], "application/x-gzip");
|
assert.equal(res.headers["content-type"], "application/x-gzip");
|
||||||
assert.equal(res.headers["content-disposition"], "attachment; filename*=utf-8''views.tar.gz");
|
assert.equal(res.headers["content-disposition"], "attachment; filename*=utf-8''test.tar.gz");
|
||||||
|
|
||||||
res.pipe(file);
|
res.pipe(file);
|
||||||
|
|
||||||
res.on("end", function() {
|
res.on("end", function() {
|
||||||
execFile("tar", ["-zxvf", filename, "views/status.html.ejs"], {cwd: path}, function(err) {
|
execFile("tar", ["-zxvf", filename, "test/dir1/testdata1.txt"], {cwd: path}, function(err) {
|
||||||
assert.equal(err, null);
|
assert.equal(err, null);
|
||||||
assert.equal(
|
assert.equal(
|
||||||
fs.readFileSync(__dirname + "/views/status.html.ejs", "utf8"),
|
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||||
fs.readFileSync(path + "/views/status.html.ejs", "utf8")
|
fs.readFileSync(path + "/test/dir1/testdata1.txt", "utf8")
|
||||||
|
);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should download several files in same directory as tar", function(next) {
|
||||||
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
|
assert.equal(err, null);
|
||||||
|
|
||||||
|
var filename = path + "/download.tar.gz";
|
||||||
|
var file = fs.createWriteStream(filename);
|
||||||
|
http.get("http://localhost:8787/test/dir2/testdata2a.txt,/test/dir2/testdata2b.txt?download=download.tar.gz", function(res) {
|
||||||
|
res.pipe(file);
|
||||||
|
res.on("end", function() {
|
||||||
|
execFile("tar", ["-zxvf", filename], {cwd: path}, function(err) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/testdata2a.txt", "utf8")
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir2/testdata2b.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/testdata2b.txt", "utf8")
|
||||||
|
);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should download several files in different directories as tar", function(next) {
|
||||||
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
|
assert.equal(err, null);
|
||||||
|
|
||||||
|
var filename = path + "/download.tar.gz";
|
||||||
|
var file = fs.createWriteStream(filename);
|
||||||
|
http.get("http://localhost:8787/test/dir1/testdata1.txt,/test/dir2/testdata2a.txt?download=download.tar.gz", function(res) {
|
||||||
|
res.pipe(file);
|
||||||
|
res.on("end", function() {
|
||||||
|
execFile("tar", ["-zxvf", filename], {cwd: path}, function(err) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/dir1/testdata1.txt", "utf8")
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/dir2/testdata2a.txt", "utf8")
|
||||||
|
);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should download as zip", function(next) {
|
||||||
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
|
var filename = path + "/download.zip";
|
||||||
|
var file = fs.createWriteStream(filename);
|
||||||
|
http.get("http://localhost:8787/?download=download.zip", function(res) {
|
||||||
|
assert.equal(res.headers["content-type"], "application/zip");
|
||||||
|
assert.equal(res.headers["content-disposition"], "attachment; filename*=utf-8''download.zip");
|
||||||
|
|
||||||
|
res.pipe(file);
|
||||||
|
|
||||||
|
res.on("end", function() {
|
||||||
|
execFile("unzip", [filename, "c9.vfs.server/download.js"], {cwd: path}, function(err, stdout, stderr) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/download.js", "utf8"),
|
||||||
|
fs.readFileSync(path + "/c9.vfs.server/download.js", "utf8")
|
||||||
|
);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should download sub directory as zip", function(next) {
|
||||||
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
|
assert.equal(err, null);
|
||||||
|
|
||||||
|
var filename = path + "/download.zip";
|
||||||
|
var file = fs.createWriteStream(filename);
|
||||||
|
http.get("http://localhost:8787/test?download=download.zip", function(res) {
|
||||||
|
res.pipe(file);
|
||||||
|
|
||||||
|
res.on("end", function() {
|
||||||
|
execFile("unzip", [filename, "test/dir1/testdata1.txt"], {cwd: path}, function(err) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/test/dir1/testdata1.txt", "utf8")
|
||||||
|
);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should download several files in same directory as zip", function(next) {
|
||||||
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
|
assert.equal(err, null);
|
||||||
|
|
||||||
|
var filename = path + "/download.zip";
|
||||||
|
var file = fs.createWriteStream(filename);
|
||||||
|
http.get("http://localhost:8787/test/dir2/testdata2a.txt,/test/dir2/testdata2b.txt?download=download.zip", function(res) {
|
||||||
|
res.pipe(file);
|
||||||
|
res.on("end", function() {
|
||||||
|
execFile("unzip", [filename], {cwd: path}, function(err) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/testdata2a.txt", "utf8")
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir2/testdata2b.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/testdata2b.txt", "utf8")
|
||||||
|
);
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should download several files in different directories as zip", function(next) {
|
||||||
|
tmp.dir({unsafeCleanup: true}, function(err, path) {
|
||||||
|
path = path.replace(/\w:/, '');
|
||||||
|
assert.equal(err, null);
|
||||||
|
|
||||||
|
var filename = path + "/download.zip";
|
||||||
|
var file = fs.createWriteStream(filename);
|
||||||
|
http.get("http://localhost:8787/test/dir1/testdata1.txt,/test/dir2/testdata2a.txt?download=download.zip", function(res) {
|
||||||
|
res.pipe(file);
|
||||||
|
res.on("end", function() {
|
||||||
|
execFile("unzip", [filename], {cwd: path}, function(err) {
|
||||||
|
assert.equal(err, null);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/dir1/testdata1.txt", "utf8")
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
fs.readFileSync(__dirname + "/test/dir2/testdata2a.txt", "utf8"),
|
||||||
|
fs.readFileSync(path + "/dir2/testdata2a.txt", "utf8")
|
||||||
);
|
);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
test data 1
|
|
@ -0,0 +1 @@
|
||||||
|
test data 2a
|
|
@ -0,0 +1 @@
|
||||||
|
test data 2b
|
Ładowanie…
Reference in New Issue