使用 Node.js 将上传的文件流转发到另一台服务器

发布于 2025-01-10 20:35:17 字数 2942 浏览 3 评论 0原文

我正在构建一个连接到 DMS (Alfresco) 的 Angular / Node.js 应用程序。服务器端 Node.js / Express 层充当代理,向客户端隐藏 Alfresco 的复杂性:

Angular client <--> Node backend <--> Alfresco

这个问题仅与 Node.js 后端有关。

上传文件时,我想将传入的文件直接转发到 Alfresco,而不临时将其存储在磁盘上。使用临时磁盘存储,这可以按预期工作:

const fileUpload = require('express-fileupload');
const FormData = require('form-data');

// app is the express object
app.use(fileUpload({ createParentPath: true }));

app.post('/file', async (req, res) => {

    // filedata is the FormData field of the client containing the actual file
    let file = req.files.filedata;
    let tmpPath = __dirname + '/tmp/' + file.name;

    // save the file temporarily on the server
    file.mv(tmpPath, async function(err) {

        // create a new FormData Object for the upload to the DMS
        const formData = new FormData();
        formData.append('name', name);
        // creates an fs.ReadStream object which is inherited from stream.Readable
        formData.append('filedata', fs.createReadStream(tmpPath));
    
        // upload the file to the DMS
        let response = await axios.post('/files/...', formData, { headers: formData.getHeaders() });
        
        // remove the temporary file
        fs.rm(tmpPath, () => {

            // return the answer of the DMS to the client
            res.send(response.data);
        });
    });
});

现在我想避免磁盘访问并将文件直接转发到 DMS。考虑到 在 Node.js 中将缓冲区转换为 ReadableStream 我尝试了以下三种替代方案。

const { Readable } = require('stream');

app.post('/file', async (req, res) => {
    let file = req.files.fileData;

    // create a new FormData Object for the upload to the DMS
    const formData = new FormData();
    formData.append('name', name);

    /* alternatives starting here */
    // Source: https://stackoverflow.com/questions/13230487/
    
    // #1
    const readable = new Readable();
    readable._read = () => {};
    readable.push(file.data);
    readable.push(null);
    
    // #2
    const readable = new Readable();
    readable._read = () => {};
    const buffer = new Buffer(file.data, 'utf-8');
    readable.push(buffer);  
    readable.push(null);
    
    // #3
    const readable = Readable.from(file.data);

    /* alternatives ending here */

    // put the Readable into the FormData object
    formData.append('filedata', readable);

    // upload the file to the DMS
    let response = await axios.post('/files/...', formData, { headers: formData.getHeaders() });

    // return the answer of the DMS to the client
    res.send(response.data);
});

无论我尝试什么选择,Alfresco 总是抱怨,必填字段会丢失。尽管如此,还是提供了所有必填字段,因为临时存储文件的示例工作正常。我认为,Alfresco 无法处理我提供的流,并且我无法完全理解流在这种情况下如何工作。我应该采取什么不同的做法?

请注意,为了可读性,所有错误处理以及 Alfresco 请求配置/API URL 均被省略。

I'm building an Angular / Node.js application connected to a DMS (Alfresco). The server side Node.js / Express layer acts as a proxy to hide the complexity of Alfresco from the client:

Angular client <--> Node backend <--> Alfresco

This question is only about the Node.js backend.

When uploading a file I would like to forward the incoming file directly to Alfresco without temporarily storing it on the disk. With temporary disk storage this works as expected:

const fileUpload = require('express-fileupload');
const FormData = require('form-data');

// app is the express object
app.use(fileUpload({ createParentPath: true }));

app.post('/file', async (req, res) => {

    // filedata is the FormData field of the client containing the actual file
    let file = req.files.filedata;
    let tmpPath = __dirname + '/tmp/' + file.name;

    // save the file temporarily on the server
    file.mv(tmpPath, async function(err) {

        // create a new FormData Object for the upload to the DMS
        const formData = new FormData();
        formData.append('name', name);
        // creates an fs.ReadStream object which is inherited from stream.Readable
        formData.append('filedata', fs.createReadStream(tmpPath));
    
        // upload the file to the DMS
        let response = await axios.post('/files/...', formData, { headers: formData.getHeaders() });
        
        // remove the temporary file
        fs.rm(tmpPath, () => {

            // return the answer of the DMS to the client
            res.send(response.data);
        });
    });
});

Now I would like to avoid the disk access and forward the file directly to the DMS. Taking into consideration Converting a Buffer into a ReadableStream in Node.js I tried the following three alternatives.

const { Readable } = require('stream');

app.post('/file', async (req, res) => {
    let file = req.files.fileData;

    // create a new FormData Object for the upload to the DMS
    const formData = new FormData();
    formData.append('name', name);

    /* alternatives starting here */
    // Source: https://stackoverflow.com/questions/13230487/
    
    // #1
    const readable = new Readable();
    readable._read = () => {};
    readable.push(file.data);
    readable.push(null);
    
    // #2
    const readable = new Readable();
    readable._read = () => {};
    const buffer = new Buffer(file.data, 'utf-8');
    readable.push(buffer);  
    readable.push(null);
    
    // #3
    const readable = Readable.from(file.data);

    /* alternatives ending here */

    // put the Readable into the FormData object
    formData.append('filedata', readable);

    // upload the file to the DMS
    let response = await axios.post('/files/...', formData, { headers: formData.getHeaders() });

    // return the answer of the DMS to the client
    res.send(response.data);
});

Whatever alternative I try, Alfresco always complains, required fields would be missing. Nonetheless, all required fields are provided, since the example with storing the file temporarily works fine. I think, Alfresco cannot handle the stream I provide and that I have a problem to completely understand how streams work in this situation. What should I do differently?

Please note, that all error handling as well as Alfresco request configuration / API URL is omitted for the sake of readability.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

假面具 2025-01-17 20:35:17

尝试提供文件相关信息,例如文件名、knownLength 等。

let file = req.files.fileData;

const formData = new FormData();
// buffer with file related info
formData.append(name, file.data, {
    filename: file.name,
    contentType: file.mimetype,
    knownLength: file.size
});

// upload the file to the DMS
let response = await axios.post('/files/...', formData, {headers: formData.getHeaders() });

// return the answer of the DMS to the client
res.send(response.data);

try providing file related information such as filename, knownLength etc.

let file = req.files.fileData;

const formData = new FormData();
// buffer with file related info
formData.append(name, file.data, {
    filename: file.name,
    contentType: file.mimetype,
    knownLength: file.size
});

// upload the file to the DMS
let response = await axios.post('/files/...', formData, {headers: formData.getHeaders() });

// return the answer of the DMS to the client
res.send(response.data);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文