可以同时下载文件并将其上传到另一个源而不存储吗?

huangapple go评论158阅读模式
英文:

Is it possible to download a file and upload it simultaneously to another source without storage?

问题

I am looking for a nodeJS boilerplate, if technically possible, to download a given file and upload it simultaneously as it downloads.

这意味着在上传之前并不完全下载或存储整个文件,因此内存使用不应该是相关的问题。

我理解流可能是解决方案,但我不确定在源和目标要求方面意味着什么(例如,是否需要多部分支持)?

英文:

I am looking for a nodeJS boiler plate, if technically possible, to download a given file and upload it simultaneously as it downloads.

It implies that the whole file isn’t downloaded nor stored entirely before being uploaded, so memory usage shouldn’t be relevant here.

I understand that streams may be the solution, but I am unsure what does it imply in terms of source and destination requirements (multipart support for instance) ?

答案1

得分: 1

You can do this with the built-in http module and streams.

const http = require("http");

const request = (options = {}) => {
    options = Object.assign({
        method: "GET"
    }, options);
    return http.request(options);
};

let download = request({
    url: "http://127.0.0.1:80",
    path: "/bytes/100"
});

// handle get/download response
download.on("response", (res) => {
    console.log("Response from download", res.headers);
    
    let upload = request({
        url: "http://127.0.0.1:80",
        path: "/anything/foo",
        method: "POST"
    });

    // handle post/upload response
    upload.on("response", (res) => {
        let chunks = [];
        res.on("data", (chunk) => {
            chunks.push(chunk);
        });
        res.on("end", () => {
            console.log("Body", Buffer.concat(chunks).toString());
        });
    });

    // pipe download to upload
    res.pipe(upload);
});

download.end();

I used the httpbin container from "kennethreitz".
docker run -p 80:80 kennethreitz/httpbin: https://httpbin.org/

In the "upload response," you can see that the uploaded data is encoded as base64 for debugging purposes, but it's the exact same data you received from "/bytes/100".

Example code for comparing download with upload buffer:

download.on("response", (res) => {
    console.log("Response from download", res.headers);

    let recvBuffer = Buffer.alloc(0);
    let sendBuffer = Buffer.alloc(0);

    let upload = request({
        url: "http://127.0.0.1:80",
        path: "/anything/foo",
        method: "POST"
    });

    let chunks = [];

    res.on("data", (chunk) => {
        chunks.push(chunk);
    });

    res.on("end", () => {
        recvBuffer = Buffer.concat(chunks);
    });

    // handle post/upload response
    upload.on("response", (res) => {
        console.log("Response from upload", res.headers)

        let chunks = [];

        res.on("data", (chunk) => {
            chunks.push(chunk)
        });

        res.on("end", () => {
            // parse response as json & extract received/send data
            let json = JSON.parse(Buffer.concat(chunks).toString());
            sendBuffer = Buffer.from(json.data.split(",")[1], "base64");

            console.log("Download = Upload:", Buffer.compare(recvBuffer, sendBuffer) === 0);
        });
    });

    // pipe download to upload
    res.pipe(upload);
});

Since it uses streams, the memory footprint is pretty low.

Note that the exact solution depends on your targets that provide the download/uploads endpoints. But since you wanted a boilerplate, this should be a good start for you.

英文:

You can do this with the build in http module and streams.

const http = require("http");


const request = (options = {}) => {

    options = Object.assign({
        method: "GET"
    }, options);

    return http.request(options);

};


let download = request({
    url: "http://127.0.0.1:80",
    path: "/bytes/100"
});

// handle get/download response
download.on("response", (res) => {

    console.log("Response from download", res.headers);

    let upload = request({
        url: "http://127.0.0.1:80",
        path: "/anything/foo",
        method: "POST"
    });


    // handle post/upload response
    upload.on("response", (res) => {

        let chunks = [];

        res.on("data", (chunk) => {
            chunks.push(chunk)
        });

        res.on("end", () => {
            console.log("Body", Buffer.concat(chunks).toString())
        });

    });

    // pipe download to upload
    res.pipe(upload);

});

download.end();

I used the httpbin container from "kennethreitz".
docker run -p 80:80 kennethreitz/httpbin: https://httpbin.org/

The example code above "downloads" 100 bytes, and pipe them to "/anything/foo", which response with debug information about the made request.

Example output:

Response from download {
server: 'gunicorn/19.9.0',
date: 'Thu, 20 Apr 2023 07:43:08 GMT',
connection: 'close',
'content-type': 'application/octet-stream',
'content-length': '100',
'access-control-allow-origin': '*',
'access-control-allow-credentials': 'true'
}
Response from upload {
server: 'gunicorn/19.9.0',
date: 'Thu, 20 Apr 2023 07:43:08 GMT',
connection: 'close',
'content-type': 'application/json',
'content-length': '456',
'access-control-allow-origin': '*',
'access-control-allow-credentials': 'true'
}
Body {
"args": {}, 
"data": "data:application/octet-stream;base64,KnyWTt134iwvCP8AHAx7eXfPrxpjZUuZMiqUI3y/PAemFqBmAGDZNI7IlP5oQ+pUjKYaKPXH3CjI0HeaSrGefPtztVsJh+R+BR8UaCQAzGCpyCS/fR34k26AnG4b+jK8D1A6vA==", 
"files": {}, 
"form": {}, 
"headers": {
"Connection": "close", 
"Host": "localhost", 
"Transfer-Encoding": "chunked"
}, 
"json": null, 
"method": "POST", 
"origin": "192.168.16.1", 
"url": "http://localhost/anything/foo"
}

In the "upload response" you can see that the uploaded data is encoded as base64 for debugging purpose, but its the exact same data you received from "/bytes/100"

Example code for comparing download with upload buffer:

<!-- begin snippet: js hide: true console: false babel: false -->

<!-- language: lang-js -->

download.on(&quot;response&quot;, (res) =&gt; {
console.log(&quot;Response from download&quot;, res.headers);
let recvBuffer = Buffer.alloc(0);
let sendBuffer = Buffer.alloc(0);
let upload = request({
url: &quot;http://127.0.0.1:80&quot;,
path: &quot;/anything/foo&quot;,
method: &quot;POST&quot;
});
let chunks = [];
res.on(&quot;data&quot;, (chunk) =&gt; {
chunks.push(chunk);
});
res.on(&quot;end&quot;, () =&gt; {
recvBuffer = Buffer.concat(chunks);
});
// handle post/upload response
upload.on(&quot;response&quot;, (res) =&gt; {
console.log(&quot;Response from upload&quot;, res.headers)
let chunks = [];
res.on(&quot;data&quot;, (chunk) =&gt; {
chunks.push(chunk)
});
res.on(&quot;end&quot;, () =&gt; {
// pase response as json &amp; extract received/send data
let json = JSON.parse(Buffer.concat(chunks).toString());
sendBuffer = Buffer.from(json.data.split(&quot;,&quot;)[1], &quot;base64&quot;);
console.log(&quot;Download = Upload:&quot;, Buffer.compare(recvBuffer, sendBuffer) === 0);
});
});
// pipe download to upload
res.pipe(upload);
});

<!-- end snippet -->

Since it uses streams, the memory footprint is pretty low.

Note that the exact solution is depending on your targets that provide the download/uploads endpoints. But since you wanted a boilerplate this should be a good start for you.

huangapple
  • 本文由 发表于 2023年4月19日 15:48:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76051934.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定