NodeJS - 请求文件并将其压缩
NodeJS - Request file and zip it
我目前正在为我的个人网站创建 REST API。我想包括一些下载,我想提供选择多个下载并将它们下载为 zip 文件的可能性。
我的第一种方法非常简单:带有 url 的数组,请求每个 url,压缩它,发送给用户,删除。但是,我认为这种方法太脏了,因为周围有像流这样的东西似乎很适合这个东西。
现在,我四处尝试,目前正在努力解决在不同范围内处理流和事件的基本概念。
以下有效:
const r = request(url, options);
r.on('response', function(res) {
res.pipe(fs.createWriteStream('./file.jpg'));
});
根据我的理解,在这种情况下 r 是一个传入流,我会监听它的响应事件,一旦它发生,我就会将它通过管道传输到一个流中,我用它来写入文件系统。
我的第一步是重构它,使其更适合我的情况,但我在这里已经失败了:
async function downloadFile(url) {
return request({ method: 'GET', uri: url });
}
现在我想使用一个调用 "downloadFile()" 和不同 url 的函数,并再次使用 createWriteStream() 将所有这些文件保存到磁盘:
const urls = ['https://download1', 'https://download2', 'https://download3'];
urls.forEach(element => {
downloadFile(element).then(data => {
data.pipe(fs.createWriteStream('file.jpg'));
});
});
使用调试器我发现 "response" 事件在数据对象中不存在——也许这已经是问题所在了?此外,我认为 data.body 包含我下载的文档(在本例中为 pdf)的字节,所以我想知道我是否可以将其流式传输到其他地方?
阅读一些 stackoveflow 线程后,我发现了以下模块:archiver
阅读此线程:Dynamically create and stream zip to client
@dankohn 建议采用这样的方法:
archive
.append(fs.createReadStream(file1), { name: 'file1.txt' })
.append(fs.createReadStream(file2), { name: 'file2.txt' });
让我假设我需要能够从我的数据对象中提取流才能继续。
我是在错误的轨道上还是在根本上犯了错误?
编辑:lmao 感谢您解决我的问题,我不知道发生了什么
使用存档器似乎是一种有效的方法,但是在将大量数据从网络输入到 zip 存档时,建议使用流。否则,整个存档数据将需要保存在内存中。
archiver 不支持从流中添加文件,但是 zip-stream does. For reading a stream from the web, request 派上用场了。
例子
// npm install -s express zip-stream request
const request = require('request');
const ZipStream = require('zip-stream');
const express = require('express');
const app = express();
app.get('/archive.zip', (req, res) => {
var zip = new ZipStream()
zip.pipe(res);
var stream = request('https://loremflickr.com/640/480')
zip.entry(stream, { name: 'picture.jpg' }, err => {
if(err)
throw err;
})
zip.finalize()
});
app.listen(3000)
更新:使用多个文件的示例
在zip.entry()
的回调函数中添加递归处理下一个文件的例子
app.get('/archive.zip', (req, res) => {
var zip = new ZipStream()
zip.pipe(res);
var queue = [
{ name: 'one.jpg', url: 'https://loremflickr.com/640/480' },
{ name: 'two.jpg', url: 'https://loremflickr.com/640/480' },
{ name: 'three.jpg', url: 'https://loremflickr.com/640/480' }
]
function addNextFile() {
var elem = queue.shift()
var stream = request(elem.url)
zip.entry(stream, { name: elem.name }, err => {
if(err)
throw err;
if(queue.length > 0)
addNextFile()
else
zip.finalize()
})
}
addNextFile()
})
使用Async/Await
你可以将它封装成一个使用 async/await 的承诺,比如:
await new Promise((resolve, reject) => {
zip.entry(stream, { name: elem.name }, err => {
if (err) reject(err)
resolve()
})
})
zip.finalize()
我目前正在为我的个人网站创建 REST API。我想包括一些下载,我想提供选择多个下载并将它们下载为 zip 文件的可能性。 我的第一种方法非常简单:带有 url 的数组,请求每个 url,压缩它,发送给用户,删除。但是,我认为这种方法太脏了,因为周围有像流这样的东西似乎很适合这个东西。
现在,我四处尝试,目前正在努力解决在不同范围内处理流和事件的基本概念。
以下有效:
const r = request(url, options);
r.on('response', function(res) {
res.pipe(fs.createWriteStream('./file.jpg'));
});
根据我的理解,在这种情况下 r 是一个传入流,我会监听它的响应事件,一旦它发生,我就会将它通过管道传输到一个流中,我用它来写入文件系统。
我的第一步是重构它,使其更适合我的情况,但我在这里已经失败了:
async function downloadFile(url) {
return request({ method: 'GET', uri: url });
}
现在我想使用一个调用 "downloadFile()" 和不同 url 的函数,并再次使用 createWriteStream() 将所有这些文件保存到磁盘:
const urls = ['https://download1', 'https://download2', 'https://download3'];
urls.forEach(element => {
downloadFile(element).then(data => {
data.pipe(fs.createWriteStream('file.jpg'));
});
});
使用调试器我发现 "response" 事件在数据对象中不存在——也许这已经是问题所在了?此外,我认为 data.body 包含我下载的文档(在本例中为 pdf)的字节,所以我想知道我是否可以将其流式传输到其他地方?
阅读一些 stackoveflow 线程后,我发现了以下模块:archiver
阅读此线程:Dynamically create and stream zip to client
@dankohn 建议采用这样的方法:
archive
.append(fs.createReadStream(file1), { name: 'file1.txt' })
.append(fs.createReadStream(file2), { name: 'file2.txt' });
让我假设我需要能够从我的数据对象中提取流才能继续。
我是在错误的轨道上还是在根本上犯了错误?
编辑:lmao 感谢您解决我的问题,我不知道发生了什么
使用存档器似乎是一种有效的方法,但是在将大量数据从网络输入到 zip 存档时,建议使用流。否则,整个存档数据将需要保存在内存中。
archiver 不支持从流中添加文件,但是 zip-stream does. For reading a stream from the web, request 派上用场了。
例子
// npm install -s express zip-stream request
const request = require('request');
const ZipStream = require('zip-stream');
const express = require('express');
const app = express();
app.get('/archive.zip', (req, res) => {
var zip = new ZipStream()
zip.pipe(res);
var stream = request('https://loremflickr.com/640/480')
zip.entry(stream, { name: 'picture.jpg' }, err => {
if(err)
throw err;
})
zip.finalize()
});
app.listen(3000)
更新:使用多个文件的示例
在zip.entry()
的回调函数中添加递归处理下一个文件的例子
app.get('/archive.zip', (req, res) => {
var zip = new ZipStream()
zip.pipe(res);
var queue = [
{ name: 'one.jpg', url: 'https://loremflickr.com/640/480' },
{ name: 'two.jpg', url: 'https://loremflickr.com/640/480' },
{ name: 'three.jpg', url: 'https://loremflickr.com/640/480' }
]
function addNextFile() {
var elem = queue.shift()
var stream = request(elem.url)
zip.entry(stream, { name: elem.name }, err => {
if(err)
throw err;
if(queue.length > 0)
addNextFile()
else
zip.finalize()
})
}
addNextFile()
})
使用Async/Await
你可以将它封装成一个使用 async/await 的承诺,比如:
await new Promise((resolve, reject) => {
zip.entry(stream, { name: elem.name }, err => {
if (err) reject(err)
resolve()
})
})
zip.finalize()