Nodejs 使用formidable模块实现stream方式图片上传,报错

Nodejs 使用formidable模块实现stream方式图片上传,报错

我是一nodejs新手,老板要求我能用nodejs实现stream方式上传图片,google良久,发现formidable很好用,也很受人们喜爱,然后在google找到许多例子,但是总是实现不了

 var form = new formidable.IncomingForm();
 form.parse( request, function( error, fields, files ) {
    console.log( "Completed Parsing" );

    if( error ){
        response.writeHead( 500, { "Content-Type" : "text/plain" } );
        response.end( "CRAP! " + error + "\n" );
        return;
    }
}

在这里的时候 总是报错 Error: MultipartParser.end(): stream ended unexpectedly: state = START,在github上有相关问题的讨论,研究良久仍不得其解,望各位帮忙,谢谢。


7 回复

Node.js 使用 Formidable 模块实现 Stream 方式图片上传时遇到错误

背景

我在尝试使用 Node.js 的 formidable 模块来处理通过 HTTP POST 请求上传的图片文件。尽管我在网上找到了一些示例代码,但在实际操作中遇到了一些问题。

错误信息

在执行过程中,我遇到了以下错误:

Error: MultipartParser.end(): stream ended unexpectedly: state = START

示例代码及问题分析

首先,我们来看一下基本的代码框架:

const http = require('http');
const formidable = require('formidable');

http.createServer((request, response) => {
    if (request.url === '/upload' && request.method.toLowerCase() === 'post') {
        const form = new formidable.IncomingForm();

        form.parse(request, (error, fields, files) => {
            if (error) {
                response.writeHead(500, { "Content-Type": "text/plain" });
                response.end("CRAP! " + error + "\n");
                return;
            }

            console.log("Completed Parsing");

            // 这里可以添加处理文件的逻辑
            response.writeHead(200, { "Content-Type": "text/plain" });
            response.end("File uploaded successfully!");
        });

        return;
    }

    response.writeHead(200, { "Content-Type": "text/html" });
    response.end(`
        <form action="/upload" enctype="multipart/form-data" method="post">
            <input type="file" name="image" />
            <input type="submit" value="Upload Image" />
        </form>
    `);
}).listen(3000, () => {
    console.log('Server running at http://127.0.0.1:3000/');
});

解决方案

上述代码中,关键点在于如何正确解析请求中的文件流。错误 Error: MultipartParser.end(): stream ended unexpectedly: state = START 通常发生在解析器尚未准备好接收数据时就已经结束了。为了解决这个问题,我们可以确保在解析之前正确地处理请求的整个内容。

以下是修正后的完整代码示例:

const http = require('http');
const formidable = require('formidable');

http.createServer((request, response) => {
    if (request.url === '/upload' && request.method.toLowerCase() === 'post') {
        const form = new formidable.IncomingForm();

        form.uploadDir = "./uploads"; // 设置文件保存目录
        form.keepExtensions = true;  // 保持文件扩展名

        form.on('error', (err) => {
            response.writeHead(500, { "Content-Type": "text/plain" });
            response.end(`Error: ${err.message}\n`);
        });

        form.on('end', () => {
            response.writeHead(200, { "Content-Type": "text/plain" });
            response.end("File uploaded successfully!");
        });

        form.parse(request);

        return;
    }

    response.writeHead(200, { "Content-Type": "text/html" });
    response.end(`
        <form action="/upload" enctype="multipart/form-data" method="post">
            <input type="file" name="image" />
            <input type="submit" value="Upload Image" />
        </form>
    `);
}).listen(3000, () => {
    console.log('Server running at http://127.0.0.1:3000/');
});

关键点解释

  1. form.parse(request): 这个方法会自动处理上传的数据,并调用相应的事件处理器。
  2. form.on('error', callback): 处理任何可能发生的错误。
  3. form.on('end', callback): 当文件上传完成后触发此回调函数。

通过这种方式,我们可以更可靠地处理文件上传请求,并且避免了由于解析器状态异常而导致的错误。


没这么用过,我把我的一个老的例子贴出来,希望有点用。测了一下,还运行正常。

var http = require('http');
var util = require('util');
var formidable = require('formidable');
var TEST_TMP = '/tmp';
var TEST_PORT = 8001;

var server = http.createServer(function(req, res) { if (req.url == ‘/’) { res.writeHead(200, {‘content-type’: ‘text/html’}); res.end( ‘<form action="/upload" enctype=“multipart/form-data” method=“post”>’+ ‘<input type=“text” name=“title”><br>’+ ‘<input type=“file” name=“upload” multiple=“multiple”><br>’+ ‘<input type=“submit” value=“Upload”>’+ ‘</form>’ ); } else if (req.url == ‘/upload’) { var files = []; var fields = []; var form = new formidable.IncomingForm(); form.uploadDir = TEST_TMP; form.on(‘field’, function(field, value) { console.log(‘field event:’, field, value); fields.push([field, value]); }); form.on(‘file’, function(field, file) { console.log(‘file event:’, field, file); files.push([field, file]); }); form.on(‘end’, function() { util.puts(’-> upload done’); res.writeHead(200, {‘content-type’: ‘text/plain’}); res.write(‘received fields:\n\n ‘+util.inspect(fields)); res.write(’\n\n’); res.end('received files:\n\n '+util.inspect(files)); }); form.parse(req); } else { res.writeHead(404, {‘content-type’: ‘text/plain’}); res.end(‘404’); } });

server.listen(TEST_PORT); util.puts(‘listening on http://localhost:’ + TEST_PORT + ‘/’);

默认的formidable会将文件部分先保存到服务器的临时目录下面,然后在通过fs的方式进行后续操作。如果你只需要保存到服务端文件系统,那么可以在formidable里面指定临时路径位置就好了。但是如果你想保存到别的设备,比如其他的能够接受stream的地方(例如分布式文件系统),那么需要重载form.onPart方法,然后判断if(part.filename),表明这是一个文件,那么part参数就是stream了。

直接使用express3就行了。它包括了formidable

最新版本的 express3 已经用 https://github.com/superjoe30/node-multiparty 别误导人家了…

根据你的描述,错误信息 Error: MultipartParser.end(): stream ended unexpectedly: state = START 表明在解析 multipart/form-data 请求时,请求流意外结束。这可能是由于客户端请求没有正确地发送完整的文件数据。

以下是一个使用 formidable 模块处理 stream 方式上传图片的完整示例,并且会解释如何正确处理错误和文件存储。

示例代码

const http = require('http');
const formidable = require('formidable');

const server = http.createServer((req, res) => {
    if (req.url === '/upload' && req.method.toLowerCase() === 'post') {
        // 创建一个 formidable 的 IncomingForm 对象
        const form = new formidable.IncomingForm();

        // 设置文件上传目录
        form.uploadDir = './uploads';

        // 解析请求
        form.parse(req, (err, fields, files) => {
            if (err) {
                res.writeHead(500, { 'Content-Type': 'text/plain' });
                res.end(`Error: ${err}\n`);
                return;
            }

            res.writeHead(200, { 'Content-Type': 'text/plain' });
            res.end('File uploaded successfully.\n');
        });

        // 监听文件上传事件,确保文件被正确写入
        form.on('fileBegin', (name, file) => {
            // 修改文件名(可选)
            file.path = `${form.uploadDir}/${Date.now()}-${file.name}`;
        });

        // 错误处理
        form.on('error', err => {
            console.error('Error during file upload:', err);
            res.writeHead(500, { 'Content-Type': 'text/plain' });
            res.end(`Error: ${err}\n`);
        });

        return;
    }

    res.writeHead(404, { 'Content-Type': 'text/plain' });
    res.end('Not Found\n');
});

server.listen(3000, () => {
    console.log('Server running at http://localhost:3000/');
});

解释

  1. 创建 HTTP 服务器:使用 http.createServer() 创建一个简单的 HTTP 服务器。
  2. 设置文件上传路径:通过 form.uploadDir 设置文件上传目录。
  3. 解析请求:使用 form.parse() 方法解析 multipart 请求,处理表单字段和上传的文件。
  4. 文件上传开始事件:监听 fileBegin 事件来处理文件上传开始时的逻辑,例如修改文件名或路径。
  5. 错误处理:监听 error 事件以捕获并处理上传过程中的任何错误。
  6. 启动服务器:使用 server.listen() 启动服务器,监听端口。

确保你的前端表单正确配置为 enctype="multipart/form-data",并且发送的数据格式正确,这样才能避免 Error: MultipartParser.end(): stream ended unexpectedly 错误。

回到顶部