Request: ์˜ค๋ฅ˜: ์‘๋‹ต์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ธ ํ›„์—๋Š” ํŒŒ์ดํ”„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์— ๋งŒ๋“  2014๋…„ 04์›” 30์ผ  ยท  29์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: request/request

"์‘๋‹ต์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ธ ํ›„์—๋Š” ํŒŒ์ดํ”„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."๋ผ๋Š” ๋ฉ”์‹œ์ง€๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. WriteStream์œผ๋กœ ํŒŒ์ดํ”„ํ•˜๋ ค๊ณ  ํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ request() ํ˜ธ์ถœ์ด ์ด๋ฃจ์–ด์ง„ ์‹œ์ ๊ณผ readStream.pipe(writeStream) ํ˜ธ์ถœ์ด ์ด๋ฃจ์–ด์ง„ ์‹œ์  ์‚ฌ์ด์— ์ง€์—ฐ์ด ๋ฐœ์ƒํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋…ธ๋“œ ๋ฒ„์ „: v0.10.24
์š”์ฒญ ๋ฒ„์ „: 2.34.0

๋ฌธ์ œ๋ฅผ ์žฌํ˜„ํ•˜๋Š” ์†Œ์Šค๋กœ github repo๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.
https://github.com/joe-spanning/request-error

ํŽธ์˜๋ฅผ ์œ„ํ•ด ์ฝ”๋“œ๋„ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

var fs = require('fs'),
  request = require('request');

var readStream = request({
  url: 'https://gist.githubusercontent.com/joe-spanning/6902070/raw/8967b351aec744a2bb51c16cb847c44636cf53d9/pipepromise.js'
});

// wait for 5 seconds, then pipe
setTimeout(function() {

  var writeStream = readStream.pipe(fs.createWriteStream('test.js'));

  writeStream.on('finish', function() {
    console.log('all done!');
  });

  writeStream.on('error', function(err) {
    console.error(err);
  });

}, 5000);

์˜ค๋ฅ˜๋ฅผ ์žฌํ˜„ํ•˜๋ ค๋ฉด ์‹œ๊ฐ„ ์ดˆ๊ณผ ๊ฐ’์œผ๋กœ ์žฌ์ƒํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ดˆ๊ณผ๊ฐ€ ๊ธธ์ˆ˜๋ก ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•  ํ™•๋ฅ ์ด ๋†’์•„์ง‘๋‹ˆ๋‹ค.

/request-error/node_modules/request/request.js:1299
      throw new Error("You cannot pipe after data has been emitted from the re
            ^
Error: You cannot pipe after data has been emitted from the response.
    at Request.pipe (/request-error/node_modules/request/request.js:1299:13)
    at null._onTimeout (/request-error/pipe-error.js:16:32)
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

@joepie91 ๋งจ ์œ„์— ์žˆ๋Š” ์›๋ณธ ์˜ˆ์ œ๋ฅผ ๊ฐ€์ ธ

var fs = require('fs'),
  request = require('request');

var readStream = request({
  url: 'https://gist.githubusercontent.com/joe-spanning/6902070/raw/8967b351aec744a2bb51c16cb847c44636cf53d9/pipepromise.js'
})
.pipe(require('stream').PassThrough());  // this line is the only modification

// wait for 5 seconds, then pipe
setTimeout(function() {

  var writeStream = readStream.pipe(fs.createWriteStream('test.js'));

  writeStream.on('finish', function() {
    console.log('all done!');
  });

  writeStream.on('error', function(err) {
    console.error(err);
  });

}, 5000);

๋ชจ๋“  29 ๋Œ“๊ธ€

๋˜ํ•œ ๋น„์Šทํ•œ ์ƒํ™ฉ์—์„œ ์ด๊ฒƒ์„ ๋ด…๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์˜๋ฏธ๋Š” ์—†์ง€๋งŒ request.js ๋ฅผ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ถ”๊ฐ€ํ•˜๋‹ค:

var superOn = Request.prototype.on;
Request.prototype.on = function (eventName) {
  if (eventName === "data") {
    this.resume()
  }
  superOn.apply(this, arguments)
}

dataStream.on("data" ํ˜ธ์ถœ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

dataStream.on("data", function (chunk) {
      var emitted = self.emit("data", chunk)
      if (emitted) {
        self._destdata = true
      } else {
        // pause URL stream until we pipe it
        dataStream.pause()
        dataStream.unshift(chunk)
      }
})

๊ธฐ๋ณธ์ ์ธ ๋ฌธ์ œ(๋‚ด ์ƒ๊ฐ์—)๋Š” onResponse ๊ฐ€ resume() ํ˜ธ์ถœํ•˜๊ณ  "data" ๋ฆฌ์Šค๋„ˆ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š”๋ฐ, ๋‘˜ ๋‹ค ๋ฐ์ดํ„ฐ ํ๋ฆ„์„ ์‹œ์ž‘ํ•˜์ง€๋งŒ ์•„์ง ํŒŒ์ดํ”„ํ•  ์ค€๋น„๊ฐ€ ๋˜์ง€ ์•Š์•˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋‚ด ์†”๋ฃจ์…˜์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ด๋ ค๊ณ  ํ•˜์ง€๋งŒ ์•„์ง ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์—ฐ๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋ณธ ์ฝ๊ธฐ ์ŠคํŠธ๋ฆผ์„ ์ผ์‹œ ์ค‘์ง€ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ด ์†”๋ฃจ์…˜์„ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•„๋‹ˆ, ๋‚˜์—๊ฒŒ ํšจ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ํ™œ๋™์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— request ์‚ฌ์šฉ์„ ์ค‘๋‹จํ•˜๊ณ  Node.js http ๋ชจ๋“ˆ๋กœ ๋Œ์•„๊ฐ€๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‚ด ์ˆ˜์ • ์‚ฌํ•ญ์€ ์š”์ฒญ 2.37.0์— ๋Œ€ํ•œ ๊ฒƒ์ž„์„ ์–ธ๊ธ‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „ ๋ฆด๋ฆฌ์Šค์—์„œ๋Š” ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@aldeed ์ €์žฅ์†Œ๋ฅผ ๋ถ„๊ธฐํ•˜๊ณ  ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค: https://github.com/joepie91/request. ์ด (์›๋ž˜) ์ €์žฅ์†Œ๊ฐ€ ๋ฏธ๋ž˜์— ์œ ์ง€๋˜๋Š” ์ •๋„์— ๋”ฐ๋ผ ๋” ๋งŽ์€ ์ˆ˜์ • ์‚ฌํ•ญ์„ ๊ฑฐ๊ธฐ์— ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„์ง ํ™•์‹คํ•˜์ง€ ์•Š๋‹ค.

์ง€๊ธˆ ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ์ด ๋ฌธ์ œ๋ฅผ ๋””๋ฒ„๊น…ํ–ˆ์ง€๋งŒ ์ด ์Šค๋ ˆ๋“œ๋ฅผ ์ฝ์„ ๋•Œ๊นŒ์ง€ ์†”๋ฃจ์…˜์„ ์ฐพ์„ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ์— ๋Œ€ํ•œ ๋ช‡ ๊ฐ€์ง€ ๊ธฐ์ˆ ์ ์ธ ๋ฐฐ๊ฒฝ ์ •๋ณด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผํ•˜๊ธฐ ์‰ฝ๋„๋ก ์กฐ๊ธˆ ๋‹จ์ˆœํ™”ํ–ˆ์Šต๋‹ˆ๋‹ค.

request ์ œ๊ณตํ•˜๋Š” ์˜๋ฏธ์—์„œ '์ŠคํŠธ๋ฆฌ๋ฐ API'๋‹น์‹  ์ˆ˜ .pipe A์™€ ์›๋ž˜ ์š”์ฒญ ๊ฐ์ฒด WritableStream . ๊ทธ๋Ÿฌ๋‚˜ ์ด ์š”์ฒญ ๊ฐ์ฒด๋Š” ์‘๋‹ต ์ŠคํŠธ๋ฆผ ์ž์ฒด๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— request ๋Š” data ์ด๋ฒคํŠธ๋ฅผ 'shims'ํ•ฉ๋‹ˆ๋‹ค - ์‘๋‹ต ์ŠคํŠธ๋ฆผ์— data ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ด ์ด๋ฒคํŠธ๋ฅผ ์ž์ฒด์ ์œผ๋กœ ๋‹ค์‹œ ๋‚ด๋ณด๋‚ด๋ฏ€๋กœ ์š”์ฒญ ๊ฐœ์ฒด๊ฐ€ ์ŠคํŠธ๋ฆผ ์ž์ฒด๋กœ ์ž‘๋™ํ•˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹จ ํ•˜๋‚˜์˜ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ์˜ data ์ด๋ฒคํŠธ์— ์—ฐ๊ฒฐํ•˜์ž๋งˆ์ž ํ๋ฅด๊ธฐ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค .

์ด์ œ ์ด๊ฒƒ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Node.js๋Š” '์ง„๋“œ๊ธฐ'์™€ ํ•จ๊ป˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ์ธํ„ฐํ”„๋ฆฌํ„ฐ์—์„œ '์ฃผ๊ธฐ'๋กœ ๊ฐ„์ฃผํ•ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์„ค๋ช…์€ ์ฝ”๋“œ์˜ ํ•œ ๋ธ”๋ก์ด 'ํ‹ฑ'์„ ์†Œ๋น„ํ•˜๊ณ  ๋‹ค์Œ ํ‹ฑ๊นŒ์ง€ ์ด๋ฒคํŠธ๊ฐ€ ํ™•์ธ๋˜๊ฑฐ๋‚˜ ์‹คํ–‰๋˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์Œ ๊ฐ€์ƒ ์ฝ”๋“œ๊ฐ€ ์ œ๋Œ€๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

stream = getSomeRequestStream();
stream.pipe(target);

๋‘ ์ค„ ๋ชจ๋‘ ๋™์ผํ•œ ํ‹ฑ์—์„œ ์‹คํ–‰๋˜๋ฉฐ ์ฒซ ๋ฒˆ์งธ data ์ด๋ฒคํŠธ๋Š” ๋‹ค์Œ ํ‹ฑ๊นŒ์ง€ ๋ฐœ์ƒํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ์ธํ„ฐํ”„๋ฆฌํ„ฐ๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค. ์ฆ‰, .pipe ๊ฐ€ ์ฒซ ๋ฒˆ์งธ data ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ _์ „์—_ ์ฒ˜๋ฆฌ๋˜๊ณ  ๋ชจ๋“  ๊ฒƒ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ @joe-spanning์ด ์‹œ๋„ํ•œ ๊ฒƒ์€ ์ด ํŠน์ • ์˜ˆ์—์„œ ์‹œ๊ฐ„ ์ดˆ๊ณผ์ธ ๋น„๋™๊ธฐ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ ์ดˆ๊ธฐํ™” ๋ฐ .pipe ๋Š” ๋” ์ด์ƒ ๋™์ผํ•œ ์ฝ”๋“œ ๋ธ”๋ก์— ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ๊ตฌ์กฐ๋Š” ์ด๋ฒคํŠธ์™€ ๋™์ผํ•œ ๊ทœ์น™์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ๋‹ค์Œ ํ‹ฑ๊นŒ์ง€ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋น„๋™๊ธฐ ๊ตฌ์กฐ๊ฐ€ ์‹คํ–‰๋  ๋•Œ ์ด๋ฒคํŠธ ์ฃผ๊ธฐ๊ฐ€ ๋ง‰ ๋๋‚˜๊ณ  ์ฒซ ๋ฒˆ์งธ data ์ด๋ฒคํŠธ๊ฐ€ _already_ ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋ฐ์ดํ„ฐ ์ฒญํฌ๋Š” ์ŠคํŠธ๋ฆผ ์ดˆ๊ธฐํ™”์™€ .pipe ์‚ฌ์ด์˜ ๊ฐ„๊ฒฉ์„ ํ†ตํ•ด ๋“ค์–ด์˜ต๋‹ˆ๋‹ค. request ๋Š” data ์ด๋ฒคํŠธ๊ฐ€ ์ด๋ฏธ ๋ฐœ์ƒํ–ˆ์Œ์„ ๊ฐ์ง€ํ•˜๊ณ  ์ŠคํŠธ๋ฆผ์ด ์ด๋ฏธ ํ๋ฅด๊ธฐ ์‹œ์ž‘ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ์ด์ƒ .pipe ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๊ณ  ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

์ŠคํŠธ๋ฆผ ์ดˆ๊ธฐํ™”๋ณด๋‹ค ํ•œ ํ‹ฑ ์ด์ƒ ๋Šฆ๊ฒŒ ๋ฐœ์ƒํ•˜๋Š” _anything_์— ๋Œ€ํ•ด์„œ๋„ ์ด์™€ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

@aldeed ์˜ ์†”๋ฃจ์…˜์€ ์š”์ฒญ ๊ฐ์ฒด์— ์—ฐ๊ฒฐ๋œ ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ  ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ๋ฐ์ดํ„ฐ ์ฒญํฌ๋ฅผ ์›๋ž˜ ์ŠคํŠธ๋ฆผ์— ๋‹ค์‹œ ๋„ฃ๊ณ  ์›๋ž˜ ์ŠคํŠธ๋ฆผ์„ ์ผ์‹œ ์ค‘์ง€ํ•˜์—ฌ ์ด ๋ฌธ์ œ๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ํ๋ฆ„์„ ์ค‘์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์Šค๋„ˆ๊ฐ€ ์—ฐ๊ฒฐ๋˜๋ฉด ์›๋ž˜ ์ŠคํŠธ๋ฆผ์„ ์žฌ๊ฐœํ•˜๊ณ  ์ด๋ฒˆ์—๋Š” data ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

data ์ด๋ฒคํŠธ shim์ด request ์—์„œ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์€ ์‹ค์ œ๋กœ ์„ค๊ณ„ ๊ฒฐํ•จ์ž…๋‹ˆ๋‹ค. ์ŠคํŠธ๋ฆผ ์ดˆ๊ธฐํ™” ๋ฐ ํŒŒ์ดํ•‘์ด ๋™์ผํ•œ ํ‹ฑ์—์„œ ๋ฐœ์ƒํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๊ณ  Node.js๊ฐ€ ๋ณธ์งˆ์ ์œผ๋กœ ๋น„๋™๊ธฐ์‹ ํ”Œ๋žซํผ์ด๋ผ๋Š” ๊ฐ€๋Šฅ์„ฑ์„ ๊ณ ๋ คํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค(๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ์ข…๋ฅ˜์˜ ์ƒํ™ฉ์ด ์˜ˆ์ƒ๋จ). ์ด ์ˆ˜์ •์ด ์ž‘๋™ํ•˜๊ณ  ์•ˆ์ •์ ์œผ๋กœ ์ž‘๋™ํ•ด์•ผ ํ•˜์ง€๋งŒ ๋” ๋‚˜์€ ์†”๋ฃจ์…˜์€ ์ฒซ ๋ฒˆ์งธ ์ˆ˜์‹ ๊ธฐ๊ฐ€ ์—ฐ๊ฒฐ๋  ๋•Œ๊นŒ์ง€ ์›๋ž˜ ์ŠคํŠธ๋ฆผ์˜ data ์ด๋ฒคํŠธ์— ๋Œ€ํ•œ ๋ฐ”์ธ๋”ฉ์„ ์—ฐ๊ธฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ์ฐธ๊ณ  ์‚ฌํ•ญ์ž…๋‹ˆ๋‹ค. ์š”์ฒญ ์ดˆ๊ธฐํ™”์—์„œ ์ฝœ๋ฐฑ์„ ์ •์˜ํ•˜๋ฉด ์ด ์ˆ˜์ • ์‚ฌํ•ญ์ด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‚ด๋ถ€์ ์œผ๋กœ ์ฝœ๋ฐฑ์ด ์ œ๊ณต๋˜๋ฉด request ๋Š” data ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ž์ฒด์— ์ฒจ๋ถ€ํ•˜๊ณ  ๋ณธ๋ฌธ์„ ์ฝ๊ณ  ์ฝœ๋ฐฑ์— ๋ณธ๋ฌธ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋Œ€์‹  response ์ด๋ฒคํŠธ์— ์—ฐ๊ฒฐํ•˜๊ณ  ์ฝœ๋ฐฑ์„ ์ง€์ •ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ด๋ฒคํŠธ๋Š” ์‘๋‹ต์ด ์ˆ˜์‹ ๋˜๊ณ  ์š”์ฒญ์ด ์ŠคํŠธ๋ฆฌ๋ฐํ•  ์ค€๋น„๊ฐ€ ๋˜๋Š” ์ฆ‰์‹œ (์•ˆ์ •์ ์œผ๋กœ) ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

@aldeed ๋ช‡ ์‹œ๊ฐ„ ๋™์•ˆ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ–ˆ์ง€๋งŒ ์‹ค์ œ๋กœ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋Š” ๋ชจ๋“  ์‹œ๋„๋Š” ์‹คํŒจํ–ˆ์œผ๋ฉฐ ์ด์ œ ๋งˆ์นจ๋‚ด ๋‚ด ์ž‘์—…์„ ๊ณ„์†ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹น์‹ ์—๊ฒŒ ๋งฅ์ฃผ๋ฅผ ๋นš์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค :)

์ƒˆ๋กœ์šด ์Šคํƒ€์ผ์˜ ์ŠคํŠธ๋ฆผ ๋ฐ 3.0 ๋ถ„๊ธฐ์™€ ๊ด€๋ จ๋œ ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ๊ฐ„๋žตํ•œ ๋ฉ”๋ชจ์ž…๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ์Šคํƒ€์ผ์˜ ์ฝ๊ธฐ ๊ฐ€๋Šฅํ•œ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๋ฉด "์ผ์‹œ ์ค‘์ง€ ๋ฐ ๋ฒ„ํผ๋ง" ์ƒํƒœ๋กœ ๊ฐ„์ฃผ๋  ์ˆ˜ ์žˆ๋Š” ์ƒํƒœ๋กœ ์—ด๋ฆฌ๋ฉฐ read() ๊ฐ€ ๋  ๋•Œ๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ๋Œ€๋ถ€๋ถ„์˜ nextTick() ํ•ดํ‚น์„ ๋ฒ„๋ ค์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์„ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์กฐ๊ธˆ ๋‹ค๋ฃจ์—ˆ๊ณ  ์šฐ๋ฆฌ๊ฐ€ ํ•ด์•ผ ํ•  ์ผ์€ ์ฒซ ๋ฒˆ์งธ read()๋ฅผ ๊ธฐ๋‹ค๋ฆฐ ๋‹ค์Œ ๋ชจ๋“  "๊ฒŒ์œผ๋ฅธ" ์ฝ”๋“œ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•œ ๊ฐ€์ง€ ๋ถ„๋ช…ํ•œ ๊ฒฐ๊ณผ๋Š” 3.0์—์„œ ์ฝœ๋ฐฑ์„ ์ „๋‹ฌํ•˜๋ฉด ๋ชจ๋“  ๋‚ด์šฉ์„ ๋ฒ„ํผ๋งํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์ŠคํŠธ๋ฆฌ๋ฐ API์— ์•ก์„ธ์Šคํ•˜๋ฉด ๋ฒ„ํผ๋ง์ด ๋น„ํ™œ์„ฑํ™”๋˜์ง€๋งŒ nextTick ํ•ดํ‚น ์—†์ด๋Š” ์ด๋ฅผ ์œ ์ถ”ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

@mikael ํ˜„์žฌ ๋งˆ์Šคํ„ฐ ๋ธŒ๋žœ์น˜์— ๋Œ€ํ•œ ํ’€ ์š”์ฒญ์œผ๋กœ ์ด ์ˆ˜์ • ์‚ฌํ•ญ์„ ์ˆ˜๋ฝํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

๊ทธ๊ฒƒ์„ ํ‰๊ฐ€ํ•˜๋ ค๋ฉด PR๋กœ ๋ณด์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ดœ์ฐฎ์€. ๊ทธ๊ฐ€ ์›๋ž˜ ์ˆ˜์ •์— ๊ธฐ์—ฌํ–ˆ๋‹ค๋Š” ์ ์„ ๊ฐ์•ˆํ•  ๋•Œ ์‹ค์ œ pull ์š”์ฒญ ์ƒ์„ฑ์„ @aldeed ์—๊ฒŒ ๋งก๊ธฐ๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์ €์žฅ์†Œ์— ๋ณ‘ํ•ฉํ•˜๋Š” ๊ฒƒ์ด ๋ผ์ด์„ ์‹ฑ ์ธก๋ฉด์—์„œ ๊ทธ์˜ ์„ ํƒ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

@joepie91 ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. @mikeal , ๋‚ด๊ฐ€ ๊ธฐ์–ตํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ 2.37.0์—์„œ ์ฝ”๋“œ๋ฅผ ์ฝ๋Š” ๋ฐฉ์‹์€ ์ด๋ฏธ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ์˜ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ ์ฒ˜์Œ์—๋Š” "์ผ์‹œ ์ค‘์ง€ ๋ฐ ๋ฒ„ํผ๋ง"๋ฉ๋‹ˆ๋‹ค. ๋ฌธ์ œ๋Š” ์ž์‹ ์˜ "๋ฐ์ดํ„ฐ" ๋ฆฌ์Šค๋„ˆ๋ฅผ ์—ฐ๊ฒฐํ•˜๊ณ  ์žฌ๊ฐœ๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ์ด ์ž‘์—…์€ readstream์— ์ผ์‹œ ์ค‘์ง€๋ฅผ ํ•ด์ œํ•˜๋„๋ก ์ง€์‹œํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์œ„์น˜์—์„œ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. @joepie91์ด ๋งํ–ˆ๋“ฏ์ด ์ง„์ •ํ•œ ์†”๋ฃจ์…˜์€ "๋ฐ์ดํ„ฐ" ๋ฆฌ์Šค๋„ˆ๋ฅผ ์—ฐ๊ฒฐํ•˜์ง€ ์•Š๋„๋ก ๋‚ด๋ถ€๋ฅผ ์žฌ์„ค๊ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋ฌธ์„œ์—์„œ :

Node v0.10์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด Readable ํด๋ž˜์Šค๊ฐ€ ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ „ Node ํ”„๋กœ๊ทธ๋žจ๊ณผ์˜ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด 'data' ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์ถ”๊ฐ€๋˜๊ฑฐ๋‚˜ pause() ๋˜๋Š” resume() ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ Readable ์ŠคํŠธ๋ฆผ์ด "ํ๋ฆ„ ๋ชจ๋“œ"๋กœ ์ „ํ™˜๋ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” ์ƒˆ๋กœ์šด read() ๋ฉ”์„œ๋“œ์™€ 'readable' ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋”๋ผ๋„ ๋” ์ด์ƒ 'data' ์ฒญํฌ ์†์‹ค์— ๋Œ€ํ•ด ๊ฑฑ์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

2.37์€ ํ™•์‹คํžˆ ์ƒˆ๋กœ์šด ์Šคํƒ€์ผ์˜ ์ŠคํŠธ๋ฆผ์„ ์‚ฌ์šฉ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. :) ๋ฐ์ดํ„ฐ ์ด๋ฒคํŠธ๋ฅผ ๋‚ด๋ณด๋‚ด๊ธฐ ์ „์— ์š”์ฒญ ๊ฐœ์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํŒŒ์ดํ”„ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ๋‹น์‹ ์ดํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค ๊ฐ™์€ ์ง„๋“œ๊ธฐ์— ์š”์ฒญ ์ธ์Šคํ„ด์Šค์— ํŒŒ์ดํ”„ ๋‹น์‹ ์ด ๊ทธ๊ฒƒ์„ ๋งŒ๋“ค๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ์œ ์‚ฌํ•œ ํŠธ๋ฆญ (์šฐ๋ฆฌ๊ฐ€ ์‹œ๋„ํ•˜๊ณ  ๊ฒฝ์šฐ์— ๋”ฐ๋ผ์ด ๋ฌธ์ œ๋ฅผ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ๋ช‡ ๊ฐ€์ง€ ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, ์ „๋ถ€๋Š” ์•„๋‹ˆ์ง€๋งŒ) ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.

์ข‹์•„, ๋‚ด๊ฐ€ ์ž˜๋ชป ์ฝ์—ˆ๊ฑฐ๋‚˜ ์ž˜๋ชป ๊ธฐ์–ตํ•˜๊ณ  ์žˆ๊ฑฐ๋‚˜ 3.0 ์ฝ”๋“œ๋„๋ณด๊ณ  ํ˜ผ๋ž€์Šค๋Ÿฌ์›Œํ–ˆ์Šต๋‹ˆ๋‹ค. :)

๊ฐ€์žฅ ๋น ๋ฅธ ์ˆ˜์ •์€ stream.PassThrough() ๊ฐœ์ฒด์— ์ฆ‰์‹œ ํŒŒ์ดํ”„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
๋‚˜์ค‘์— ๋ฌธ์ œ ์—†์ด ํ†ต๊ณผ ๊ฐœ์ฒด์—์„œ ํŒŒ์ดํ”„ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
์ฐธ์กฐ: http://nodejs.org/api/stream.html#stream_class_stream_passthrough

@ZJONSSON ์–ด๋Š ์‹œ์ ์—์„œ ์‹œ๋„ํ–ˆ์ง€๋งŒ ์ž‘๋™ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋‚ด๊ฐ€ ์ดํ•ดํ•˜๋Š” ํ•œ, ๋‹ค๋ฅธ ์ข…๋ฅ˜์˜ ์ŠคํŠธ๋ฆผ๊ณผ ๋™์ผํ•œ ์ œํ•œ(๋‚˜์ค‘ ์ŠคํŠธ๋ฆผ ์ฒจ๋ถ€ ์ด์ „์— ๋ฐฉ์ถœ๋œ ๋ฐ์ดํ„ฐ ์žฌ๋ฐฉ์ถœ ์—†์Œ)์ด ๋ฐœ์ƒํ•˜๊ธฐ ์‰ฌ์šฐ๋ฏ€๋กœ ์›๋ณธ ์ฒจ๋ถ€ ํŒŒ์ผ์ด request ๊ฒฝ์šฐ ์—ฌ์ „ํžˆ ์ค‘๋‹จ๋ฉ๋‹ˆ๋‹ค. request ๊ฐœ์ฒด์— ๋Œ€ํ•œ ์ž์‹ ์˜ ์ฒจ๋ถ€ ํŒŒ์ผ์€ ๋™์ผํ•œ ํ‹ฑ์—์„œ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

@joepie91 ๋งจ ์œ„์— ์žˆ๋Š” ์›๋ณธ ์˜ˆ์ œ๋ฅผ ๊ฐ€์ ธ

var fs = require('fs'),
  request = require('request');

var readStream = request({
  url: 'https://gist.githubusercontent.com/joe-spanning/6902070/raw/8967b351aec744a2bb51c16cb847c44636cf53d9/pipepromise.js'
})
.pipe(require('stream').PassThrough());  // this line is the only modification

// wait for 5 seconds, then pipe
setTimeout(function() {

  var writeStream = readStream.pipe(fs.createWriteStream('test.js'));

  writeStream.on('finish', function() {
    console.log('all done!');
  });

  writeStream.on('error', function(err) {
    console.error(err);
  });

}, 5000);

์†”์งํžˆ ๋ชจ๋ฅด๊ฒ ๋‹ค. ์ œ ๊ธฐ์–ต์ด ๋งž๋‹ค๋ฉด ์ •ํ™•ํžˆ ์ œ๊ฐ€ ์‹œ๋„ํ•œ ๊ฒƒ์ธ๋ฐ ๋„์›€์ด ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ ๋™์•ˆ request ์˜ ๊ธฐ๋ณธ ์ฝ”๋“œ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

์ˆ˜์ •ํ•œ ์˜ˆ์ œ๊ฐ€ ์‹ค์ œ๋กœ @joe-spanning์˜ ์˜ˆ์ œ์ž„์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ์˜ ๊ฒฝ์šฐ์—๋งŒ ์ž‘๋™ํ•˜๋Š” ์ฝ”๋“œ์™€ ์ฐจ์ด์ ์ด ์žˆ๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ œ์ถœ๋œ PR #1098

๋‚ด๋ถ€์ ์œผ๋กœ ์ฝœ๋ฐฑ์ด ์ œ๊ณต๋˜๋ฉด ์š”์ฒญ์€ ๋ฐ์ดํ„ฐ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ž์ฒด์— ์—ฐ๊ฒฐํ•˜๊ณ  ๋ณธ๋ฌธ์„ ์ฝ๊ณ  ๋ณธ๋ฌธ์„ ์ฝœ๋ฐฑ์— ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ๋Œ€์‹  >(์ฝœ๋ฐฑ์„ ์ง€์ •ํ•˜์ง€ ์•Š์Œ) ์‘๋‹ต ์ด๋ฒคํŠธ์— ์—ฐ๊ฒฐํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด ์ด๋ฒคํŠธ๋Š” ์‘๋‹ต์ด ์ˆ˜์‹ ๋˜๊ณ  ์š”์ฒญ์ด ์ŠคํŠธ๋ฆฌ๋ฐํ•  ์ค€๋น„๊ฐ€ ๋˜๋Š” ์ฆ‰์‹œ (์•ˆ์ •์ ์œผ๋กœ) ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋ฐฉ๊ธˆ request(url, mycb).pipe(mywstream) ๋ฌผ๋ ธ์Šต๋‹ˆ๋‹ค.
๋•Œ๋กœ๋Š” mycb๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๋•Œ๋กœ๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
์ฝœ๋ฐฑ์ด ์ด๋ฏธ ์„ค์ •๋œ pipe() ์‚ฌ์šฉ์„ ํ”ผํ•ด์•ผ ํ•œ๋‹ค๋Š” ์š”์ฒญ์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๊นŒ?

.pipe in .on('response') ์‹œ๋„ํ•  ๋•Œ ์ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์„ ์ œ์™ธํ•˜๋ฉด ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

req = request(options)
req.on 'response', (response) ->
  # get info from headers here
  stream = createWriteStream file
  req.pipe stream

๋‚ด ์ชฝ์—์„œ ์—…๋ฐ์ดํŠธ, ์™œ ์ „์— ์ด๊ฒƒ์„ ๊ฒŒ์‹œํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด ๋ฌธ์ œ๊ฐ€ ์‹œ์˜ ์ ์ ˆํ•˜๊ฒŒ ํ•ด๊ฒฐ๋˜์ง€ ์•Š๊ณ  request ์˜ ๋‹ค๋ฅธ ์—ฌ๋Ÿฌ ์‹ค๋ง์Šค๋Ÿฌ์šด ๋ฒ„๊ทธ์— ๊ฑธ์ณ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์–ผ๋งˆ ์ „์— ์ด ์‚ฌ์šฉ ์‚ฌ๋ก€๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ์ž์ฒด HTTP ํด๋ผ์ด์–ธํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. bhttp . ๋˜ํ•œ Streams2 ์ง€์› ๋ฌธ์ œ๋„ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

์ด์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

๋‚˜๋Š” ์ด ๋ฌธ์ œ๊ฐ€ ์˜ค๋ž˜ ๋˜์—ˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ์ œ ๊ฒฝ์šฐ์—๋Š”(

const req = request.get(URL);
req.on('response', (response) => {
    response.pause();
    // Do stuff with response.statusCode etc.
    setTimeout(() => {
        response
        .pipe(process.stdout)
        ;
    }, 2000);
});

์ฆ‰, ์‘๋‹ต ์ŠคํŠธ๋ฆผ์„ ์ผ์‹œ ์ค‘์ง€ํ•˜๊ณ ( request ํ•˜๋ฉด ์ŠคํŠธ๋ฆผ์ด ์ž๋™์œผ๋กœ ํ๋ฆ„ ๋ชจ๋“œ๋กœ ์„ค์ •๋ฉ๋‹ˆ๊นŒ?) ์‘๋‹ต ๊ฐœ์ฒด ์ž์ฒด๋ฅผ ํŒŒ์ดํ”„ํ•ฉ๋‹ˆ๋‹ค( req ์•„๋‹˜).

์ด์— ๋Œ€ํ•ด ๋‚˜๋ณด๋‹ค ์ง€์‹์ด ํ’๋ถ€ํ•œ ์‚ฌ์šฉ์ž์˜ ํ”ผ๋“œ๋ฐฑ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ๋„ ํ•œ๋ฒˆ ์ ‘ํ•ด๋ณด์„ธ์š”... ์ œ ๊ฒฝ์šฐ์—๋Š” ๋…ผ๋ฆฌ์  ์˜ค๋ฅ˜์˜€๊ธฐ์— ๋์—์„œ ํ•ด๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ณ ์œ  ID๋ฅผ ํ†ตํ•ด ๊ฐ ์š”์ฒญ์„ ์ €์žฅํ•˜๋Š” ์ „์—ญ ๋ฐฐ์—ด์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ์š”์ฒญ์„ ๋™์‹œ์— ์‹คํ–‰ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์š”์ฒญ์— ๋Œ€ํ•œ ์ค‘๋‹จ ์˜ต์…˜์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋” ๋งŽ์€ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

unzipper = function() {
    var self = this;

    /**
     * Properties
     */
    this.req = [];
    this.aborted = [];
    /* */

    /**
     * Methods
     */
    this.download = function(id, onProgress, onSuccess, onError, onAbort){

        var newinstanceAbort = {
          id: id,
          abort: false
        };

        this.aborted.push(newinstanceAbort);

        var url = '...';
        var target = '...';

        var request = require('request');
        var j = request.jar();
        var progress = require('request-progress');
        var cookie = request.cookie(app.session.sessionName + '=' + app.session.sessionId);
        j.setCookie(cookie, url);

        var downloadRequest = {
          id: id,
          request: request({url: url, jar: j})
        };

        this.req.push(downloadRequest);

        var instanceRequest = app.findObject(this.req, id);
        var instanceAbort = app.findObject(this.aborted, id);

        progress(instanceRequest.request, {
            throttle: 10,
        })
        .on('progress', function (state) {
            var progress = Math.round(state.percent * 100);
            onProgress(progress);
        })
        .on('error', function (err) {
            onError(err);
        })
        .on('end', function (e) {
            if(instanceAbort.abort) {
                onAbort();
            } else {
                onSuccess();
            }

            /**
             * Remove the unique request object from the req to complete the request
             *
             * If this is not done, the request would throw "You cannot pipe after data has been emitted from the response" as it is constantly re-emitting the same request
             */
            self.complete(id);

        })
        .pipe(app.fs.createWriteStream(target));
    };



    this.abort = function(id){
        var instanceAbort = app.findObject(this.aborted, id);
        var instanceRequest = app.findObject(this.req, id);

        instanceRequest.request.abort();
        instanceAbort.abort = true;
    };

    this.complete = function(id){
        var index = this.req.map(function(x){ return x.id; }).indexOf(id);

        this.req.splice(index, 1);
    };
    /* */

};

@julien-c ์ข‹์€ ์ „ํ™”์ž…๋‹ˆ๋‹ค. response ์ˆ˜์‹ ๊ธฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๋ฉด ์ž๋™์œผ๋กœ "ํ๋ฆ„ ๋ชจ๋“œ"๋กœ ์ „ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. [email protected] ๋ฐ [email protected] ๋ชจ๋‘์—์„œ ๋ฐœ์ƒํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์˜๋„๋œ ๊ฒƒ์ธ์ง€ ์•„๋‹Œ์ง€ ํ™•์‹คํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๋‚ด ์ดํ•ด๋Š” ์ด๊ฒƒ์ด ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค). ์ง€๊ธˆ์€ ์ŠคํŠธ๋ฆผ์„ ์ผ์‹œ ์ค‘์ง€ํ•˜๋Š” ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ๊ณ„ํš์ž…๋‹ˆ๋‹ค(์ง„ํ–‰ ์ค‘์— ์ƒˆ๋กœ์šด ๋‚ด์šฉ์„ ๋ฐœ๊ฒฌํ•˜๋ฉด ์—ฌ๊ธฐ์— ๋‹ค์‹œ ๊ฒŒ์‹œํ•˜๋„๋ก ๋…ธ๋ ฅํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค)

์œ„์˜ ์ฃผ์„ ์—์„œ dogancelik์™€ ๊ฐ™์€ ๊ฒƒ์„ ๋ฌด์ž‘์œ„๋กœ ๊ฒฝํ—˜ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์‘๋‹ต ์ฒ˜๋ฆฌ๊ธฐ ๋‚ด๋ถ€์—์„œ ํŒŒ์ดํ”„ํ•  ๋•Œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๊นŒ? request.js๋ฅผ ๋ณด๋ฉด "response" ์ด๋ฒคํŠธ๋Š” ์š”์ฒญ ์ž์ฒด์˜ "data" ํ•ธ๋“ค๋Ÿฌ(๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚ค๋Š”)๊ฐ€ ์ „ํ˜€ ์ฒจ๋ถ€๋˜๊ธฐ ์ „์— ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค:

self.emit('response', response)

self.dests.forEach(function (dest) {
  self.pipeDest(dest)
})

responseContent.on('data', function (chunk) {
  if (self.timing && !self.responseStarted) {
    self.responseStartTime = (new Date()).getTime()

    // NOTE: responseStartTime is deprecated in favor of .timings
    response.responseStartTime = self.responseStartTime
  }
  self._destdata = true
  self.emit('data', chunk)
})

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋™๊ธฐ์‹์œผ๋กœ ํ˜ธ์ถœ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์†Œ๋น„๋˜๊ธฐ ์ „์— ํŒŒ์ดํ”„๊ฐ€ ์—ฐ๊ฒฐ๋  ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ์ฝ”๋“œ์—์„œ ๋™์ผํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

let rp = require('request-promise');
let options = {uri: 'http://a-url-pointing-online-pdf-link'};

koaRouter.get('/api/url/download', async(ctx, next) => {
        try {
            ctx.set('Content-disposition', 'attachment;filename=a.pdf');
            ctx.body = rp(options);  
        } catch (e) {
            ctx.body = e;
        }
    })   

๊ทธ๋ฆฌ๊ณ  PassThrough ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ id๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€๋งŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ ์ด์œ ๋ฅผ ๋ชจ๋ฅด๊ณ  passthrough ํ•ด๊ฒฐ๋˜๋Š” ์ด์œ ๋„ ๋ชจ๋ฅด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‚ด ๋ชจ๋“  ์ฝ”๋“œ๋Š” ๋™์ผํ•œ 'ํ‹ฑ'์—์„œ ์‹คํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

let rp = require('request-promise');
let stream = require('stream');
let options = {uri: 'http://a-url-pointing-online-pdf-link'};

koaRouter.get('/api/url/download', async(ctx, next) => {
        try {
            ctx.set('Content-disposition', 'attachment;filename=a.pdf');
            let pass = stream.PassThrough();
            rp(options).pipe(pass);
            ctx.body = pass;
        } catch (e) {
            ctx.body = e;
        }
    })   

์š”์ฒญ ๊ฐ์ฒด์—๋„ ์œ ์‚ฌํ•œ ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. write ํ–ˆ๋‹ค๋ฉด pipe ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ ์ŠคํŠธ๋ฆผ์— ๋Œ€ํ•œ ํ•ต์‹ฌ http/https ๋ชจ๋“ˆ๋กœ ์ „ํ™˜ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‰ฝ๊ณ  ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ŠคํŠธ๋ฆผ์—๋Š” request ๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ „ํ™”ํ•˜์ง€ ๋งˆ์„ธ์š” ์‘๋‹ต์ด ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค์ด๊ฒƒ์€ ๋‚˜์™€ ์ž˜ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค
` var ์š”์ฒญ = require('์š”์ฒญ');
var fs = ์š”๊ตฌ('fs');
var ๊ฒฝ๋กœ = ์š”๊ตฌ('๊ฒฝ๋กœ');

๋‹ค์šด๋กœ๋“œ ํŒŒ์ผ(file_url, ๋Œ€์ƒ ๊ฒฝ๋กœ){
// ์ง„ํ–‰ ์ƒํ™ฉ์„ ์•Œ๊ธฐ ์œ„ํ•ด ๋ณ€์ˆ˜ ์ €์žฅ

var _self = this;
var received_bytes = 0;
var total_bytes = 0;

var req = request({
    method: 'GET',
    uri: file_url
});

var out = fs.createWriteStream(targetPath);
**///////////////MOVE THIS LINE  FROM HERE TO ///////////////////

/////////////// BE INSIDE req.on('์‘๋‹ต', ํ•จ์ˆ˜( ๋ฐ์ดํ„ฐ )///////
/////////////////////////req.pipe(out);/////// /////////// //////////////**

req.on('response', function ( data ) {
    **### req.pipe(out);**
    // Change the total bytes value to get progress later.
    total_bytes = parseInt(data.headers['content-length' ]);
});

req.on('data', function(chunk) {
    // Update the received bytes
    received_bytes += chunk.length;

    _self.showProgress(received_bytes, total_bytes);
    var percentage = (received_bytes * 100) / total_bytes;
    console.log(percentage + "% | " + received_bytes + " bytes out of " + total_bytes + " bytes.");

});

req.on('end', function() {
    alert("File succesfully downloaded");
});

}`

์š”์ฒญ์ด ํ˜„์žฌ ์œ ์ง€ ๊ด€๋ฆฌ ๋ชจ๋“œ์— ์žˆ์œผ๋ฉฐ ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์„ ๋ณ‘ํ•ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ #3142๋ฅผ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰