【问题标题】:How to do a follow on post of a file uploaded to a Node Express server onward to another API如何将上传到 Node Express 服务器的文件发布到另一个 API
【发布时间】:2021-08-22 19:13:07
【问题描述】:

我们在 Node 中有一个 Express 服务器,在一个路由上我们上传一个文件,然后需要将此文件继续发送到另一个端点进行处理,初始客户端无法访问该端点。大多数用例似乎依赖于从本地服务器获取路径,但在我们的例子中,该文件在本地服务器上不存在,我们将其作为上传的对象。

我尝试过使用 multer 和 file-upload 包,但在尝试上传到 3rd 方 API 时遇到了问题。两者都发布到 3rd Party API,然后失败。但是,如果我使用来自 Express 服务器的 fs 在本地上传文件,那么它会很好地上传到 API。

因此我可以先将其保存在本地,然后使用本地文件发送并删除它,但感觉应该没有必要。

使用 Multer 的代码,将文件放到 req.file 中:

    .post('/update', upload.single('file'),  async (req, res, next) => {
        try {
            const fileBuffer = req.file.buffer;
            const form = new FormData();
            form.append('file', fileBuffer);
            const boundary = form.getBoundary();
            const config = {
                headers: {
                    "Content-Type": `multipart/form-data; boundary=${boundary}`
            }};
            const axiosInst = axios.create(config);
            const url = `${baseAPI}/translate`;
            const { status, data } = await axiosInst.post(url, form);
            return res.status(status).json(data);
        } catch (error) {
            next(error);
        }
    })

使用文件上传包的代码将上传的文件放到 req.files 中:

    .post('/update', async (req, res, next) => {
        try {
            let { file } = req.files;
            fileBuffer = Buffer.from(JSON.stringify(file));
            const form = new FormData();
            form.append('file', fileBuffer);
            const boundary = form.getBoundary();
            const config = {
                headers: {
                    "Content-Type": `multipart/form-data; boundary=${boundary}`
            }};
            const axiosInst = axios.create(config);
            const url = `${baseAPI}/translate`;
            const { status, data } = await axiosInst.post(url, form);
            return res.status(status).json(data);
        } catch (error) {
            next(error);
        }
    })

我已经注销了发布到 API 的文件对象。在成功的帖子中,从节点服务器上的文件系统中获取它是:

FormData {
  _overheadLength: 237,
  _valueLength: 0,
  _valuesToMeasure: [
    ReadStream {
      _readableState: [ReadableState],
      readable: true,
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      path: 'c:\\work\\Test Template Upload Facility.docx',
      fd: null,
      flags: 'r',
      mode: 438,
      start: undefined,
      end: Infinity,
      autoClose: true,
      pos: undefined,
      bytesRead: 0,
      closed: false,
      emit: [Function],
      [Symbol(kFs)]: [Object],
      [Symbol(kCapture)]: false,
      [Symbol(kIsPerformingIO)]: false
    }
  ],
  writable: false,
  readable: true,
  dataSize: 0,
  maxDataSize: 2097152,
  pauseStreams: true,
  _released: false,
  _streams: [
    '----------------------------344728646415746168257760\r\n' +
      'Content-Disposition: form-data; name="file"; filename="Test Template Upload Facility.docx"\r\n' +
      'Content-Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document\r\n' +
      '\r\n',
    DelayedStream {
      source: [ReadStream],
      dataSize: 0,
      maxDataSize: Infinity,
      pauseStream: true,
      _maxDataSizeExceeded: false,
      _released: false,
      _bufferedEvents: [Array],
      _events: [Object: null prototype],
      _eventsCount: 1
    },
    [Function: bound ]
  ],
  _currentStream: null,
  _insideLoop: false,
  _pendingNext: false,
  _boundary: '--------------------------344728646415746168257760'
}

在使用 Multer 或文件数据方法都失败的 API 帖子中,Multer 或文件数据之间的文件对象非常相似,并按如下方式注销:

FormData {
  _overheadLength: 143,
  _valueLength: 12024,
  _valuesToMeasure: [],
  writable: false,
  readable: true,
  dataSize: 0,
  maxDataSize: 2097152,
  pauseStreams: true,
  _released: false,
  _streams: [
    '----------------------------486237832288086608829884\r\n' +
      'Content-Disposition: form-data; name="file"\r\n' +
      'Content-Type: application/octet-stream\r\n' +
      '\r\n',
    <Buffer 50 4b 03 04 14 00 06 00 08 00 00 00 21 00 df a4 d2 6c 5a 01 00 00 20 05 00 00 13 00 08 02 5b 43 6f 6e 74 65 6e 74 5f 54 79 70 65 73 5d 2e 78 6d 6c 20 ... 11974 more bytes>,
    [Function: bound ]
  ],
  _currentStream: null,
  _insideLoop: false,
  _pendingNext: false,
  _boundary: '--------------------------486237832288086608829884'
}

我当然可以将文件保存在本地,并且可能必须这样做,但如果有人能找到避免这样做的方法,我将不胜感激,谢谢。

【问题讨论】:

    标签: javascript node.js express file-upload multer


    【解决方案1】:

    我看到两件事:

    1. 您正在构建表单,但最终您发送的是文件,而不是表单。将您的代码更改为:await axiosInst.post(url, form)

    2. 在 Node.js 中将文件附加到表单时,您必须使用第三个参数(文件名)。以 multer 为例,你会这样做:form.append('file', fileBuffer, req.file.originalname);

    我写了一篇关于如何Send a File With Axios in Node.js的文章。它涵盖了一些需要避免的常见陷阱,我认为您会学到一些有用的东西。

    【讨论】:

    • 谢谢,但是我已经在做await axiosInst.post(url, form) 并发送构造的表单。我从服务方法复制到单个块中以发布此问题,并且在服务方法中传入的表单参数命名为文件,当我将其放入单个块中以在此处发布问题时忘记重命名代码。所以我已经在做(1)。关于(2),我通过使用 fs 从服务器附加一个本地文件进行了测试,在其中我没有指定第三个参数并且它工作正常,所以不确定第三个参数是否关键但会尝试一下,谢谢
    • 很高兴地说,将第三个参数提供给 form.append 已经奏效。奇怪的是,当使用 fs 从节点服务器上的本地路径附加文件时,即使没有指定 form.append 的第三个参数,它也可以工作。因此,这导致认为附加行没问题。谢谢。
    • 很高兴它成功了!确实很奇怪,我也遇到过一些事情,因此提到了它。文件缓冲区似乎需要第三个参数,但流不需要?或者您是否使用 fs 将文件读入缓冲区?那会更奇怪。
    • 我使用了fs.createReadStream,所以它是一个流。我怀疑如果我将它读入缓冲区,它就不会起作用。因为它与流一起工作,所以它误导我认为附加行很好。我认为当表单数据检测到它是一个缓冲区并且没有给出第三个参数时抛出错误会很有帮助,因为这会使检测问题变得更加容易。事实上我刚才已经建议了这个github.com/form-data/form-data/issues/509
    • 啊,这确实是一个流。不知道为什么流不需要第三个参数而是缓冲。就像你说的,这种不一致的行为至少应该被记录下来。干得好!
    【解决方案2】:

    问题是缺少文件名参数,正如@Maximorlov 在他的第二点中正确指出的那样。

    如果有人想在不通过 cmets 阅读的情况下看到正确的解决方案,我将其放在下面。

        .post('/update', upload.single('file'),  async (req, res, next) => {
            try {
                const fileBuffer = req.file.buffer;
                const form = new FormData();
                form.append('file', fileBuffer, req.file.originalname);
                const boundary = form.getBoundary();
                const config = {
                    headers: {
                        "Content-Type": `multipart/form-data; boundary=${boundary}`
                }};
                const axiosInst = axios.create(config);
                const url = `${baseAPI}/translate`;
                const { status, data } = await axiosInst.post(url, form);
                return res.status(status).json(data);
            } catch (error) {
                next(error);
            }
        })
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      • 2021-10-14
      • 1970-01-01
      • 2020-07-24
      • 1970-01-01
      • 2019-01-24
      • 2017-11-02
      相关资源
      最近更新 更多