Strongloop 环回存储服务:如何使用 StorageService.downloadStream() 下载容器中所有照片的 ZIP 文件?
Strongloop loopback-storage-service: how to use StorageService.downloadStream() to download a ZIP of all photos in container?
我正在使用 loopback-component-storage 和文件系统提供商来上传照片。它很好用。但现在我请求将 'export/download' 张照片压缩成一个 zip 存档。
我整理了一些代码来添加另一种方法,从文件存储示例中 downloadContainer()
到 Container
。它使用 Archiver
模块,一切似乎都工作正常,但浏览器在我调用 zip.finalize()
后崩溃 我希望得到一个 save file
对话框...
到目前为止,这是我的代码:
Container.downloadContainer = function(container, files, res, cb) {
var DELIM, _appendFile, _finalize, _oneComplete, _remaining, filenames, storageService, zip;
zip = Archiver('zip');
zip.pipe(res);
storageService = this;
_remaining = {};
_appendFile = function(zip, container, filename) {
var reader;
console.log('appendFile=' + filename);
reader = storageService.downloadStream(container, filename, function(stream) {
return console.log('storageService.downloadStream() resp=', _.keys(stream));
});
zip.append(reader, {
name: filename
});
};
zip.on('error', function(err) {
console.log('zip entry error', err);
res.status(500).send({
error: err.message
});
});
zip.on('entry', function(o) {
return _oneComplete(o.name);
});
_oneComplete = function(filename) {
delete _remaining[filename];
console.log('_oneComplete(): ', {
remaining: _.keys(_remaining),
size: zip.pointer()
});
if (_.isEmpty(_remaining)) {
_finalize();
}
};
_finalize = function() {
console.log('calling zip.finalize() ...');
res.on('close', function() {
console.log('response closed');
res.attachment(container + '.zip');
return res.status(200).send('OK').end();
});
zip.finalize();
};
if (files === 'all' || _.isEmpty(files)) {
console.log('files=', files);
storageService.getFiles(container, function(err, ssFiles) {
_remaining = _.object(_.pluck(ssFiles, 'name'));
console.log('filenames=', _.keys(_remaining));
return ssFiles.forEach(function(file) {
_appendFile(zip, container, file.name);
});
});
}
}
这是我在控制台中看到的内容
// log
files= all
filenames= [ 'IMG_0799.PNG', 'IMG_0800.PNG', 'IMG_0801.PNG', 'IMG_0804.PNG' ]
appendFile=IMG_0799.PNG
appendFile=IMG_0800.PNG
appendFile=IMG_0801.PNG
appendFile=IMG_0804.PNG
_oneComplete(): { remaining: [ 'IMG_0800.PNG', 'IMG_0801.PNG', 'IMG_0804.PNG' ],
size: 336110 }
_oneComplete(): { remaining: [ 'IMG_0801.PNG', 'IMG_0804.PNG' ], size: 460875 }
_oneComplete(): { remaining: [ 'IMG_0804.PNG' ], size: 1506464 }
_oneComplete(): { remaining: [], size: 1577608 }
calling zip.finalize() ...
// then browser crash
我以前没有这样做过,但我认为你的评论是正确的。
像这样的东西应该可以工作:
var AWS = app.dataSources.amazon;
var container = 'c1';
app.get('/export/download', function (req, res) {
var zip = Archiver('zip');
// create the Archiver and pipe it to our client response.
zip.pipe(res);
// ask AWS for all the files in the container
AWS.getFiles(container, function (err, files) {
files.forEach(function (file) {
// append each file stream to the zip archive
zip.append(AWS.download({
container: container,
remote: file
}), { name : file });
});
// I think finalize should end the stream and notify the client
zip.finalize();
});
});
让我知道进展如何!
此代码扩展了上述 Bryan 的建议并适用于 provider=filesystem
Archiver = require('archiver')
module.exports = function(Container) {
Container.downloadContainer = function(container, files, res, cb) {
var DELIM, _appendFile, _finalize, _oneComplete, _remaining, filenames, storageService, zip, zipFilename;
zip = Archiver('zip');
zipFilename = container + '.zip';
storageService = this;
_remaining = {};
_appendFile = function(zip, container, filename) {
var reader;
reader = storageService.downloadStream(container, filename);
zip.append(reader, {
name: filename
});
console.log("appending", {
name: filename
});
};
res.on('close', function() {
console.log('Archive wrote %d bytes', zip.pointer());
return res.status(200).send('OK').end();
});
res.attachment(zipFilename);
zip.pipe(res);
zip.on('error', function(err) {
console.log('zip entry error', err);
res.status(500).send({
error: err.message
});
});
zip.on('entry', function(o) {
return _oneComplete(o.name);
});
_oneComplete = function(filename) {
delete _remaining[filename];
console.log('_oneComplete(): ', {
remaining: _.keys(_remaining),
size: zip.pointer()
});
if (_.isEmpty(_remaining)) {
_finalize();
}
};
_finalize = function() {
console.log('calling zip.finalize() ...');
zip.finalize();
};
if (files === 'all' || _.isEmpty(files)) {
console.log('downloadContainer, files=', files);
storageService.getFiles(container, function(err, ssFiles) {
_remaining = _.object(_.pluck(ssFiles, 'name'));
return ssFiles.forEach(function(file) {
_appendFile(zip, container, file.name);
});
});
} else {
DELIM = ',';
filenames = files.split(DELIM);
_remaining = _.object(filenames);
console.log('filenames=', _.keys(_remaining));
_.each(filenames, function(filename) {
_appendFile(zip, container, filename);
});
}
};
Container.remoteMethod('downloadContainer', {
shared: true,
accepts: [
{
arg: 'container',
type: 'string',
'http': {
source: 'path'
}
}, {
arg: 'files',
type: 'string',
required: false,
'http': {
source: 'path'
}
}, {
arg: 'res',
type: 'object',
'http': {
source: 'res'
}
}
],
returns: [],
http: {
verb: 'get',
path: '/:container/downloadContainer/:files'
}
});
我正在使用 loopback-component-storage 和文件系统提供商来上传照片。它很好用。但现在我请求将 'export/download' 张照片压缩成一个 zip 存档。
我整理了一些代码来添加另一种方法,从文件存储示例中 downloadContainer()
到 Container
。它使用 Archiver
模块,一切似乎都工作正常,但浏览器在我调用 zip.finalize()
后崩溃 我希望得到一个 save file
对话框...
到目前为止,这是我的代码:
Container.downloadContainer = function(container, files, res, cb) {
var DELIM, _appendFile, _finalize, _oneComplete, _remaining, filenames, storageService, zip;
zip = Archiver('zip');
zip.pipe(res);
storageService = this;
_remaining = {};
_appendFile = function(zip, container, filename) {
var reader;
console.log('appendFile=' + filename);
reader = storageService.downloadStream(container, filename, function(stream) {
return console.log('storageService.downloadStream() resp=', _.keys(stream));
});
zip.append(reader, {
name: filename
});
};
zip.on('error', function(err) {
console.log('zip entry error', err);
res.status(500).send({
error: err.message
});
});
zip.on('entry', function(o) {
return _oneComplete(o.name);
});
_oneComplete = function(filename) {
delete _remaining[filename];
console.log('_oneComplete(): ', {
remaining: _.keys(_remaining),
size: zip.pointer()
});
if (_.isEmpty(_remaining)) {
_finalize();
}
};
_finalize = function() {
console.log('calling zip.finalize() ...');
res.on('close', function() {
console.log('response closed');
res.attachment(container + '.zip');
return res.status(200).send('OK').end();
});
zip.finalize();
};
if (files === 'all' || _.isEmpty(files)) {
console.log('files=', files);
storageService.getFiles(container, function(err, ssFiles) {
_remaining = _.object(_.pluck(ssFiles, 'name'));
console.log('filenames=', _.keys(_remaining));
return ssFiles.forEach(function(file) {
_appendFile(zip, container, file.name);
});
});
}
}
这是我在控制台中看到的内容
// log
files= all
filenames= [ 'IMG_0799.PNG', 'IMG_0800.PNG', 'IMG_0801.PNG', 'IMG_0804.PNG' ]
appendFile=IMG_0799.PNG
appendFile=IMG_0800.PNG
appendFile=IMG_0801.PNG
appendFile=IMG_0804.PNG
_oneComplete(): { remaining: [ 'IMG_0800.PNG', 'IMG_0801.PNG', 'IMG_0804.PNG' ],
size: 336110 }
_oneComplete(): { remaining: [ 'IMG_0801.PNG', 'IMG_0804.PNG' ], size: 460875 }
_oneComplete(): { remaining: [ 'IMG_0804.PNG' ], size: 1506464 }
_oneComplete(): { remaining: [], size: 1577608 }
calling zip.finalize() ...
// then browser crash
我以前没有这样做过,但我认为你的评论是正确的。
像这样的东西应该可以工作:
var AWS = app.dataSources.amazon;
var container = 'c1';
app.get('/export/download', function (req, res) {
var zip = Archiver('zip');
// create the Archiver and pipe it to our client response.
zip.pipe(res);
// ask AWS for all the files in the container
AWS.getFiles(container, function (err, files) {
files.forEach(function (file) {
// append each file stream to the zip archive
zip.append(AWS.download({
container: container,
remote: file
}), { name : file });
});
// I think finalize should end the stream and notify the client
zip.finalize();
});
});
让我知道进展如何!
此代码扩展了上述 Bryan 的建议并适用于 provider=filesystem
Archiver = require('archiver')
module.exports = function(Container) {
Container.downloadContainer = function(container, files, res, cb) {
var DELIM, _appendFile, _finalize, _oneComplete, _remaining, filenames, storageService, zip, zipFilename;
zip = Archiver('zip');
zipFilename = container + '.zip';
storageService = this;
_remaining = {};
_appendFile = function(zip, container, filename) {
var reader;
reader = storageService.downloadStream(container, filename);
zip.append(reader, {
name: filename
});
console.log("appending", {
name: filename
});
};
res.on('close', function() {
console.log('Archive wrote %d bytes', zip.pointer());
return res.status(200).send('OK').end();
});
res.attachment(zipFilename);
zip.pipe(res);
zip.on('error', function(err) {
console.log('zip entry error', err);
res.status(500).send({
error: err.message
});
});
zip.on('entry', function(o) {
return _oneComplete(o.name);
});
_oneComplete = function(filename) {
delete _remaining[filename];
console.log('_oneComplete(): ', {
remaining: _.keys(_remaining),
size: zip.pointer()
});
if (_.isEmpty(_remaining)) {
_finalize();
}
};
_finalize = function() {
console.log('calling zip.finalize() ...');
zip.finalize();
};
if (files === 'all' || _.isEmpty(files)) {
console.log('downloadContainer, files=', files);
storageService.getFiles(container, function(err, ssFiles) {
_remaining = _.object(_.pluck(ssFiles, 'name'));
return ssFiles.forEach(function(file) {
_appendFile(zip, container, file.name);
});
});
} else {
DELIM = ',';
filenames = files.split(DELIM);
_remaining = _.object(filenames);
console.log('filenames=', _.keys(_remaining));
_.each(filenames, function(filename) {
_appendFile(zip, container, filename);
});
}
};
Container.remoteMethod('downloadContainer', {
shared: true,
accepts: [
{
arg: 'container',
type: 'string',
'http': {
source: 'path'
}
}, {
arg: 'files',
type: 'string',
required: false,
'http': {
source: 'path'
}
}, {
arg: 'res',
type: 'object',
'http': {
source: 'res'
}
}
],
returns: [],
http: {
verb: 'get',
path: '/:container/downloadContainer/:files'
}
});