Merge pull request +13816 from c9/pull/core/299

Support for downloading workspace files as a zip archive
pull/309/head
Harutyun Amirjanyan 2016-05-12 02:00:34 +04:00
commit 79e53ea790
6 zmienionych plików z 274 dodań i 48 usunięć

Wyświetl plik

@ -2,7 +2,7 @@ define(function(require, exports, module) {
"use strict";
main.consumes = [
"Plugin", "c9", "ui", "menus", "tree", "info", "vfs"
"Plugin", "c9", "ui", "menus", "tree", "info", "vfs", "preferences", "settings"
];
main.provides = ["download"];
return main;
@ -15,7 +15,12 @@ define(function(require, exports, module) {
var tree = imports.tree;
var vfs = imports.vfs;
var info = imports.info;
var prefs = imports.preferences;
var settings = imports.settings;
var SETTING_NAME = "downloadFilesAs";
var SETTING_PATH = "user/general/@" + SETTING_NAME;
/***** Initialization *****/
var plugin = new Plugin("Ajax.org", main.consumes);
@ -40,6 +45,28 @@ define(function(require, exports, module) {
onclick: download
}), 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() {
@ -55,25 +82,40 @@ define(function(require, exports, module) {
if (node.isFolder && node.path == "/")
downloadProject();
else if (paths.length > 1)
vfs.download(paths);
downloadPaths(paths);
else if (node.isFolder)
downloadFolder(node.path);
else
downloadFile(node.path);
}
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) {
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) {
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 *****/

Wyświetl plik

@ -80,41 +80,62 @@ define(function(require, exports, module) {
});
}
else {
// TODO add support for downloding as zip on windows
// var cwd;
// var args = ["-r", "-"];
// paths.forEach(function(path) {
// if (!path) return;
// var dir = Path.dirname(path);
// if (!cwd) cwd = dir;
// var name = Path.relative(cwd, path);
// if (name[0] == "-") name = "./" + name;
// args.push(name);
// });
// vfs.spawn("zip", { args: args, cwd: cwd }
var args = ["-zcf", "-"];
paths.forEach(function(path) {
var executable, args, contentType;
if (/\.zip$/.test(filename)) {
executable = "zip";
args = ["-r", "-", "--"];
contentType = "application/zip"
}
else {
executable = "tar";
args = ["-zcf", "-", "--"];
contentType = "application/x-gzip"
}
// Find the longest common parent directory of all the paths.
var cwd = null;
paths.forEach(function (path) {
if (!path) return;
var dir = Path.dirname(path);
var name = Path.basename(path);
if (name[0] == "-")
name = "--add-file=" + name;
args.push("-C" + dir, name);
if (!cwd) {
cwd = dir;
}
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)
return next(err);
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) {
if (res.headerSent)
return;
res.writeHead(200, {
"Content-Type": "application/x-gzip",
"Content-Type": contentType,
"Content-Disposition": filenameHeader
});
res.write(data);
@ -125,7 +146,7 @@ define(function(require, exports, module) {
process.stderr.on("data", function (data) {
stderr += data;
});
process.on("exit", function(code, signal) {
if (res.headerSent)
return;
@ -133,16 +154,16 @@ define(function(require, exports, module) {
var err;
if (code == 127) {
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" +
" 'sudo apt-get install tar'");
" 'sudo apt-get install " + executable + "'");
} else if (code) {
err = new error.InternalServerError(
"'tar' utility failed with exit code " + code +
"'" + executable + "' utility failed with exit code " + code +
" and stderr:/n'" + stderr + "'");
} else if (signal) {
err = new error.InternalServerError(
"'tar' utility was terminated by signal " + signal
"'" + executable + "' utility was terminated by signal " + signal
);
}

Wyświetl plik

@ -53,8 +53,9 @@ describe(__filename, function(){
describe("download", function() {
it("should download", function(next) {
it("should download as tar", function(next) {
tmp.dir({unsafeCleanup: true}, function(err, path) {
path = path.replace(/\w:/, '');
var filename = path + "/download.tar.gz";
var file = fs.createWriteStream(filename);
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) {
path = path.replace(/\w:/, '');
assert.equal(err, null);
var filename = path + "/download.tar.gz";
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.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(
fs.readFileSync(__dirname + "/views/status.html.ejs", "utf8"),
fs.readFileSync(path + "/views/status.html.ejs", "utf8")
fs.readFileSync(__dirname + "/test/dir1/testdata1.txt", "utf8"),
fs.readFileSync(path + "/test/dir1/testdata1.txt", "utf8")
);
next();
});
@ -102,22 +104,180 @@ describe(__filename, function(){
it("should download without specifying a name", 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/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-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.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(
fs.readFileSync(__dirname + "/views/status.html.ejs", "utf8"),
fs.readFileSync(path + "/views/status.html.ejs", "utf8")
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 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();
});

Wyświetl plik

@ -0,0 +1 @@
test data 1

Wyświetl plik

@ -0,0 +1 @@
test data 2a

Wyświetl plik

@ -0,0 +1 @@
test data 2b