kopia lustrzana https://github.com/c9/core
Merge pull request +13816 from c9/pull/core/299
Support for downloading workspace files as a zip archivepull/309/head
commit
79e53ea790
|
@ -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,6 +15,11 @@ 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 *****/
|
||||
|
||||
|
@ -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,7 +82,7 @@ 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
|
||||
|
@ -64,17 +91,32 @@ define(function(require, exports, module) {
|
|||
}
|
||||
|
||||
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 *****/
|
||||
|
||||
plugin.on("load", function(){
|
||||
|
|
|
@ -80,29 +80,50 @@ 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 executable, args, contentType;
|
||||
|
||||
var args = ["-zcf", "-"];
|
||||
paths.forEach(function(path) {
|
||||
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);
|
||||
|
||||
|
@ -114,7 +135,7 @@ define(function(require, exports, module) {
|
|||
return;
|
||||
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "application/x-gzip",
|
||||
"Content-Type": contentType,
|
||||
"Content-Disposition": filenameHeader
|
||||
});
|
||||
res.write(data);
|
||||
|
@ -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
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
test data 1
|
|
@ -0,0 +1 @@
|
|||
test data 2a
|
|
@ -0,0 +1 @@
|
|||
test data 2b
|
Ładowanie…
Reference in New Issue