"์๋ต์์ ๋ฐ์ดํฐ๋ฅผ ๋ด๋ณด๋ธ ํ์๋ ํ์ดํํ ์ ์์ต๋๋ค."๋ผ๋ ๋ฉ์์ง๊ฐ ํ์๋๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. 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)
๋ํ ๋น์ทํ ์ํฉ์์ ์ด๊ฒ์ ๋ด ๋๋ค.
๋ค๋ฅธ ์๋ฏธ๋ ์์ง๋ง 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๋ฅผ ์ฐธ์กฐํ์ญ์์ค.
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
@joepie91 ๋งจ ์์ ์๋ ์๋ณธ ์์ ๋ฅผ ๊ฐ์ ธ