如何使用node.js superagent发送multipart/form-data数据

29

我正在尝试在我的superagent post请求中发送content-type为multipart/form-data。

var myagent = superagent.agent();

myagent
  .post('http://localhost/endpoint')
  .set('api_key', apikey)
  .set('Content-Type', 'multipart/form-data')
  .send(fields)
  .end(function(error, response){
    if(error) { 
       console.log("Error: " + error);
    }
  });

我得到的错误是:

TypeError:参数必须是字符串

如果我删除:

.set('Content-Type', 'multipart/form-data')

我没有收到任何错误,但我的后端接收到的请求是 content-type: application/json。

我该如何强制内容类型为 multipart/form-data,以便我可以访问 req.files()?


如果您尝试执行文档中的示例之一,但未设置内容类型并重复使用“field”(正如该部分中的最后一个示例),会发生什么情况?这可能不是您最终想要实现的,但它可以帮助调试出错的问题。 - David Weldon
4个回答

39

首先,您没有提到以下两点:

.set('Content-Type', 'multipart/form-data')

或者

.type('form')

其次,您不使用.send,而是使用.field(name, value)

示例

假设您想要发送一个包含以下内容的表单数据请求:

  • 两个文本字段:namephone
  • 一个文件:photo

那么您的请求将类似于以下内容:

superagent
  .post( 'https://example.com/api/foo.bar' )
  .set('Authorization', '...')
  .accept('application/json')
  .field('name', 'My name')
  .field('phone', 'My phone')
  .attach('photo', 'path/to/photo.gif')

  .then((result) => {
    // process the result here
  })
  .catch((err) => {
    throw err;
  });

假设你想将JSON作为字段的值发送,那么你需要这样做。

try {
  await superagent
         .post( 'https://example.com/api/dog.crow' )
         .accept('application/json')
         .field('data', JSON.stringify({ name: 'value' }))
}
catch ( ex ) {
    // .catch() stuff
}

// .then() stuff...

对我来说,在前端,.attach('photo', 'path/to/photo.gif') 不起作用,它会报错:parameter 2 is not of type 'Blob'。我必须使用文件描述符,然后一切都正常了。 - Will59

16

它不再工作了。我得到了MIME类型为application/x-www-form-urlencoded,类型为('form')。这与文档一致。 - Overdrivr

8

不清楚您正在发送的fields变量中包含了什么内容,但以下信息可能会帮助您确定问题所在。

首先,如果您确实尝试构建一个多部分请求,这是官方文档:http://visionmedia.github.com/superagent/#multipart-requests

至于您遇到的错误...

原因是在准备请求的过程中,SuperAgent检查要发送的数据是否为字符串。如果不是字符串,它会根据'Content-Type'的值尝试对数据进行序列化,如下所示:

exports.serialize = {
  'application/x-www-form-urlencoded': qs.stringify,
  'application/json': JSON.stringify
};

这里使用了:

// body
if ('HEAD' != method && !req._headerSent) {
  // serialize stuff
  if ('string' != typeof data) {
    var serialize = exports.serialize[req.getHeader('Content-Type')];
    if (serialize) data = serialize(data);
  }

  // content-length
  if (data && !req.getHeader('Content-Length')) {
    this.set('Content-Length', Buffer.byteLength(data));
  }
}

这意味着手动设置表单的“Content-Type”,您需要使用

.set('Content-Type', 'application/x-www-form-urlencoded')

或者像risyasin提到的那样,使用.type('form')

其他任何“Content-Type”都不会被序列化,如果您的fields变量的值不是字符串,则Buffer.byteLength(data)将随后抛出TypeError: Argument must be a string异常。


-2

这是对我有效的方法。我有一个单一字段表单,用于上传文件。我将该表单转换为HTML5 FormData元素,然后进行如下操作:

var frm = new FormData(document.getElementById('formId'));
var url =  'url/here';

superagent.post(url)                    
.attach('fieldInFormName', frm.get('fieldInFormName'))                                        
.end( function (error, response) {
    //handle response
});

请注意,我尝试了多种手动设置超级代理中的“Content-Type”的方式,但由于需要在“Content-Type”中使用多部分标识符,因此从未正确地工作过。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接