【问题标题】:Using request.getAsync from bluebird, how to 'pipe' to a file使用来自 bluebird 的 request.getAsync,如何“管道”到文件
【发布时间】:2018-09-07 23:04:48
【问题描述】:

我正在尝试异步获取某些 pdf 文件的内容。为此,我将Promise.mapSeries 与来自bluebirdrequest.getAsyncspread 一起使用。

但是在then 中,我需要直接使用pipecreateWriteStream 来获得request 的结果。比如:

request(url).pipe(fs.createWriteStream(file));

这是代码,我正在使用:

const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'), { multiArgs: true });
const fs = Promise.promisifyAll(require("fs"));

const urls = ['http://localhost/test-pdf/one.pdf', 'http://localhost/test-pdf/two.pdf'];

Promise.mapSeries(urls, url => {
    return request.getAsync({url: url, encoding:'binary'}).spread((response, body) => {
        if (response.statusCode == 200){
            let r = {};
            r.name = url.match(/\/([^/]*)$/)[1]; // get the last part of url (file name)
            r.content = body;
            console.log(`Getting ${r.name}`);
            return r;
        }
        else if (response.statusCode == 404){
            console.log(`The archive ${url.match(/\/([^/]*)$/)[1]} does not exists`);
        }
        else throw new Error(`Unsuccessful attempt. Code: ${response.statusCode}`);
    });
}).then((result) => {
    // Here I want to 'pipe' to a file the result from 'getAsync'
}).catch((error) =>{
    console.error(error);
})

我的问题:

如何使用pipe 函数将getAsync 的结果管道 到文件中?有可能吗?

PD:我知道我可以使用fs.promises,但只是想知道是否可以按照我发帖的方式进行

【问题讨论】:

  • 作为评论 - 在下面的答案和 cmets 之后 - 我只想引用 here 所说的话:“STREAMING THE RESPONSE(例如 .pipe(...) ) 是DISCOURAGED,因为 Request-Promise 会不必要地增加大型请求的内存占用。为此请使用原始请求库。您可以在同一个项目中使用这两个库"

标签: javascript node.js asynchronous promise bluebird


【解决方案1】:

我认为答案已经在问题中,.then() 似乎就是您所寻找的.pipe()

可能缺少的是(result) 应该是(results),即。由Promise.mapSeries(urls, ...) 产生的所有{name, content} 对组成的数组。

Promise.mapSeries(urls, url => {
    return request.getAsync({'url':url, 'encoding':'binary'}).spread((response, body) => {
        if (response.statusCode == 200) {
            return {
                'name': url.match(/\/([^/]*)$/)[1], // get the last part of url (file name)
                'content': body
            };
        } else if (response.statusCode == 404) {
            throw new Error(`The archive ${url.match(/\/([^/]*)$/)[1]} does not exist`);
        } else {
            throw new Error(`Unsuccessful attempt. Code: ${response.statusCode}`);
        }
    });
}).then((results) => {
    // Here write each `result.content` to file.
}).catch((error) => {
    console.error(error);
});

实际上,您可能不会选择以这种方式编写它,因为每个getAsync() 都需要在任何写入开始之前完成。

在大多数情况下(可能是您想要的)更好的流程是尽快编写每个成功的 getAsync() 的内容:

Promise.mapSeries(urls, url => {
    let name = url.match(/\/([^/]*)$/)[1]; // get the last part of url (file name)
    return request.getAsync({'url':url, 'encoding':'binary'}).spread((response, body) => {
        if (response.statusCode == 200) {
            // write `body.content` to file.
        } else if (response.statusCode == 404) {
            throw new Error(`The archive ${name} does not exist`);
        } else {
            throw new Error(`Unsuccessful attempt. Code: ${response.statusCode}`);
        }
    });
}).catch((error) => {
    console.error(error);
});

更进一步,您可能会选择更好地处理错误,例如您可能希望:

  • 捕获单个 url/get/write 错误
  • 编译成功/失败统计信息。

可能是这样的:

Promise.mapSeries(urls, url => {
    let name = url.match(/\/([^/]*)$/)[1] || ''; // get the last part of url (file name)
    if(!name) {
        throw new RangeError(`Error in input data for ${url}`);
    }
    return request.getAsync({'url':url, 'encoding':'binary'}).spread((response, body) => {
        if (response.statusCode == 200) {
            // write `body.content` to file.
            return { name, 'content': body };
        } else if (response.statusCode == 404) {
            throw new Error(`The archive ${name} does not exist`);
        } else {
            throw new Error(`Unsuccessful attempt. Code: ${response.statusCode}`);
        }
    })
    .catch(error => ({ name, error }));
}).then((results) => {
    let successes = results.filter(res => !res.error).length;
    let failures = results.filter(res => !!res.error).length;
    let total = results.length;
    console.log({ successes, failures, total }); // log success/failure stats
}).catch((error) => {
    console.error(error); // just in case some otherwise uncaught error slips through
});

【讨论】:

  • 感谢您的回答,有趣的一个!我赞成它,我会尝试你的想法。我稍后会回来。
  • 只需指出 first .catch,应该是 .catch( error => ({ name, error }) ); 从箭头函数返回对象有两种方法:或者在块体中添加返回 @987654334 @ 或将其标记为表达式 x => ({}) - 两者都有效且有效。
  • 好的,我试过你的代码,我喜欢它,但是....我的问题是:你可以把函数pipe放在request.getAsync(url).spread( (response,body) => { } )的哪里???有可能???
  • 您希望使用哪个pipe 函数?你在哪里读到过它?您对尚未实现的目标有何期望?
  • 好的,现在我明白了。感谢您的帮助和时间我的朋友。
猜你喜欢
  • 2014-01-31
  • 2015-12-14
  • 1970-01-01
  • 2017-02-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
  • 2021-05-07
相关资源
最近更新 更多