创建与 FormData 一起使用的可读流
Posted
技术标签:
【中文标题】创建与 FormData 一起使用的可读流【英文标题】:Creating a Readable Stream to use with FormData 【发布时间】:2018-12-26 23:30:56 【问题描述】:我正在使用API to upload a CSV file。我从一个字符串在内存中创建 CSV 文件并使用request
模块上传它。但是,我无法从字符串创建可读流。我在How to create streams from string in Node.Js 上关注了一个 SO 答案。这是我的解决方案代码:
var importResponse = function(csv, callback)
stringify(csv, function(err, output)
const s = new Readable();
s._read = () => ;
s.push(output);
s.push(null);
request.post(
headers: 'X-API-TOKEN':token, 'content-type' : 'multipart/form-data',
url: 'https://ca1.qualtrics.com/API/v3/responseimports',
formData:
surveyId: 'SV_123',
file:
value: s,
options:
contentType: 'text/csv; charset=utf-8'
, function(err, res, body)
if(err || res.statusCode !== 200)
console.log(err || "Error status code: " + res.statusCode);
console.log(body);
return;
);
);
csv
变量看起来像 [["QID1","QID2"],["1","2"]]
,而 stringify 的输出看起来像 "QID1,QID2\n,1,2\n"
。
这个解决方案给了我错误Unexpected end of input
"meta":"httpStatus":"400 - Bad Request","error":"errorMessage":"Unexpected end of input"
如果我改用memfs
,它可以正常工作
const fs = require('memfs');
var importResponse = function(csv, callback)
stringify(csv, function(err, output)
// Create file in memory
fs.writeFileSync('/data.csv', output);
request.post(
headers: 'X-API-TOKEN':token, 'content-type' : 'multipart/form-data',
url: 'https://ca1.qualtrics.com/API/v3/responseimports',
formData:
surveyId: 'SV_123',
file:
value: fs.createReadStream('/data.csv'),
options:
contentType: 'text/csv; charset=utf-8'
, function(err, res, body)
if(err || res.statusCode !== 200)
console.log(err || "Error status code: " + res.statusCode);
console.log(body);
return;
);
);
如何将stringify
的输出转换为可用于通过 api 上传的 Stream?
【问题讨论】:
我们肯定知道,stringify
来自哪里?您有需要的特定软件包吗?
看起来csv-stringify
可以自己创建流,仅供参考。您可以直接将stringify(csv)
作为“文件”流传递。
@Jacob 是的,这解决了它。将我的代码更改为var output = stringify(csv);
,现在可以使用了。还将字符串化模块更改为同步模块。谢谢!
【参考方案1】:
您似乎正在使用request
库。您可能会遇到此警告,如他们的readme 中所述:
// Pass optional meta-data with an 'options' object with style: value: DATA, options: OPTIONS
// Use case: for some types of streams, you'll need to provide "file"-related information manually.
// See the `form-data` README for more information about options: https://github.com/form-data/form-data
custom_file:
value: fs.createReadStream('/dev/urandom'),
options:
filename: 'topsecret.jpg',
contentType: 'image/jpeg'
由于您使用的是非文件流,因此只需提供一个虚拟文件名即可:
request.post(
headers: 'X-API-TOKEN':token, 'content-type' : 'multipart/form-data',
url: 'https://ca1.qualtrics.com/API/v3/responseimports',
formData:
surveyId: 'SV_123',
file:
value: s,
options:
contentType: 'text/csv; charset=utf-8',
filename: 'dummy.csv'
, function(err, res, body)
if(err || res.statusCode !== 200)
console.log(err || "Error status code: " + res.statusCode);
console.log(body);
return;
);
【讨论】:
嗨,Jacob,我尝试添加文件名(以前有),但它仍然不起作用。我想知道问题是否出在Stringify
上。我想也许他们是字符串末尾的换行符导致了它,但事实并非如此。我不确定Stringify
和memfs
是否存在问题,在保存时“修复”了问题,或者流本身是否错误,不一定是内容......如果这有意义的话。
是的,就是这个包。抱歉应该更清楚。【参考方案2】:
对于当前节点版本,示例 sn-p 不正确或可能已过时。实现可读性的一种非常简单的方法:
const s = new Readable(
encoding: 'utf8',
read(size)
// Possibly respect the requested size to make for a good consumer experience
// Otherwise:
this.push(output, 'utf8');
this.push(null); // This signals that there's no more data.
);
尊重读者意愿的方法如下:
let data = output;
const s = new Readable(
encoding: 'utf8',
read(size)
let wantsMore = true;
while (wantsMore)
const chunk = data.slice(0, size);
if (!chunk)
return void this.push(null);
wantsMore = this.push(chunk, 'utf8');
data = data.slice(size);
);
【讨论】:
感谢您的回复。我尝试添加您的 sn-p 代替前一个,仍然收到相同的Unexpected end of input
错误。
糟糕,我在发送 EOF 信号时犯了一个错误。试试我的更新。
添加了一些关于编码的注释。这很奇怪。
我想我是在注意到您使用 request
之后才知道的。 “文件”上传很奇怪。查看其他答案。
(暂时搁置,以防流也有问题,但这可能是另一个答案)。以上是关于创建与 FormData 一起使用的可读流的主要内容,如果未能解决你的问题,请参考以下文章