最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

node.js - How to POST formdata including a file field with http2 module in Node? - Stack Overflow

matteradmin6PV0评论

I create a FormData as follows (simplified):

import FormData from 'form-data';

const createForm = async () => {
  const form = new FormData();
  form.append('field1', 'stringValue1');

  form.append('fileField', fs.createReadStream(__filename), {
    contentType: 'text/javascript',
    filename: 'file.js',
  });

  return form;
};

Then to send it over HTTP/2 (cleartext):

  // app is a FastifyInstance
  await app.ready();
  const { address, port } = app.server.address();
  const client = http2.connect(`http://${address}:${port}`);
  const request = client.request({
    ':method': 'POST',
    ':path': '/path',
    'content-type': `multipart/form-data; boundary=${form.getBoundary()}`,
  });
  request.setEncoding('utf8');

  let status;
  request.on('response', (headers) => {
    status = headers[':status'];
  });
  request.on('data', (data) => { ... });

  form.pipe(request);
  request.end();

  await new Promise((resolve) => {
    request.on('end', () => {
      client.close();
      resolve();
    });
  });

Then in the Fastify server listening to requests I have

import fastify from 'fastify';
import fastifyFormbody from '@fastify/formbody';
import fastifyMultipart from '@fastify/multipart';

const app = fastify({ http2: true, logger: logLevel === 'debug' });
const fieldSizeLimit = 1024 * 1024 * 10;

void app.register(fastifyFormbody);
void app.register(fastifyMultipart, {
  attachFieldsToBody: 'keyValues',
  limits: {
    fieldSize: fieldSizeLimit,
    fileSize: fieldSizeLimit,
  },
  onFile: async (part) => {
    const destinationPath = path.join(bundlePath, 'uploads', part.filename);
    await saveMultipartFile(part, destinationPath);
    // eslint-disable-next-line no-param-reassign
    part.value = {
      filename: part.filename,
      savedFilePath: destinationPath,
      type: 'asset',
    };
  },
});

app.post('/path', async (req, res) => {
  console.log(Object.entries(req.body));
  ...
});

which worked fine with HTTP/1.1 requests. And the log shows fileField is not received. How do I make the request correctly?

I found Opening an http/2 connection and POSTing form-data which says

In the Node HTTP2 docs it shows an example of how to do this.

but it seems to be only talking about separating const client = http2.connect(...) and client.request, not a form data example.

The minimum Node version I need to support is 20, and I'm happy to switch from form-data if needed; I just had different errors with built-in FormData but maybe I was doing something wrong there.

I create a FormData as follows (simplified):

import FormData from 'form-data';

const createForm = async () => {
  const form = new FormData();
  form.append('field1', 'stringValue1');

  form.append('fileField', fs.createReadStream(__filename), {
    contentType: 'text/javascript',
    filename: 'file.js',
  });

  return form;
};

Then to send it over HTTP/2 (cleartext):

  // app is a FastifyInstance
  await app.ready();
  const { address, port } = app.server.address();
  const client = http2.connect(`http://${address}:${port}`);
  const request = client.request({
    ':method': 'POST',
    ':path': '/path',
    'content-type': `multipart/form-data; boundary=${form.getBoundary()}`,
  });
  request.setEncoding('utf8');

  let status;
  request.on('response', (headers) => {
    status = headers[':status'];
  });
  request.on('data', (data) => { ... });

  form.pipe(request);
  request.end();

  await new Promise((resolve) => {
    request.on('end', () => {
      client.close();
      resolve();
    });
  });

Then in the Fastify server listening to requests I have

import fastify from 'fastify';
import fastifyFormbody from '@fastify/formbody';
import fastifyMultipart from '@fastify/multipart';

const app = fastify({ http2: true, logger: logLevel === 'debug' });
const fieldSizeLimit = 1024 * 1024 * 10;

void app.register(fastifyFormbody);
void app.register(fastifyMultipart, {
  attachFieldsToBody: 'keyValues',
  limits: {
    fieldSize: fieldSizeLimit,
    fileSize: fieldSizeLimit,
  },
  onFile: async (part) => {
    const destinationPath = path.join(bundlePath, 'uploads', part.filename);
    await saveMultipartFile(part, destinationPath);
    // eslint-disable-next-line no-param-reassign
    part.value = {
      filename: part.filename,
      savedFilePath: destinationPath,
      type: 'asset',
    };
  },
});

app.post('/path', async (req, res) => {
  console.log(Object.entries(req.body));
  ...
});

which worked fine with HTTP/1.1 requests. And the log shows fileField is not received. How do I make the request correctly?

I found Opening an http/2 connection and POSTing form-data which says

In the Node HTTP2 docs it shows an example of how to do this.

but it seems to be only talking about separating const client = http2.connect(...) and client.request, not a form data example.

The minimum Node version I need to support is 20, and I'm happy to switch from form-data if needed; I just had different errors with built-in FormData but maybe I was doing something wrong there.

Share Improve this question edited Nov 17, 2024 at 17:35 Alexey Romanov asked Nov 17, 2024 at 12:55 Alexey RomanovAlexey Romanov 171k38 gold badges327 silver badges506 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

The solution was to replace

  form.pipe(request);
  request.end();

with

  form.pipe(request);
  form.on('end', () => { request.end(); });
Post a comment

comment list (0)

  1. No comments so far