javascriptnode.jsstreammultipartform-datauwebsockets

Reading multipartform-data stream correctly (NodeJS)


I'm trying to receive a multipartform-data stream which might include various files and fields and to write the files to a directory (uWebsockets.js server). I have this code:

    let boundary = null;
    let fields = [];
    let streams = [];
    let keep = false;
    res.onData((chunk, isLast) => {
        const buff = Buffer.concat([Buffer.from(chunk)]).toString('binary').split('\r\n');
        if (!boundary) {
            boundary = buff[0];
        }
        for (let i = 0; i < buff.length; i++) {

            const line = buff[i];
            if (i > 0 && i < buff.length - 1 && line == '') {
                keep = true;
                continue;
            }
            if (line == boundary || line == boundary + '--') {
                keep = false;
                if (streams[fields.length - 1]) {
                    streams[fields.length - 1].end();
                }
            }
            if (line == boundary) {
                fields[fields.length] = {};
            }

            if (line.includes('Content-Disposition')) {
                if (line.includes('filename="')) {
                    fields[fields.length - 1].filename = getFilename(line);
                    fields[fields.length - 1].type = 'file';
                    fields[fields.length - 1].path = path.resolve(options.uploadPath + fields[fields.length - 1].filename);
                    streams[fields.length - 1] = fs.createWriteStream(
                        path.resolve(options.uploadPath + fields[fields.length - 1].filename)
                    );
                } else {
                    fields[fields.length - 1].type = 'field';
                }
                fields[fields.length - 1].name = getField(line);
            }
            if (line.includes('Content-Type')) {
                fields[fields.length - 1].contentType = line.split('Content-Type: ')[1];
            }

            if (keep == true) {
                if (fields[fields.length - 1].filename) {
                    streams[streams.length - 1].write(Buffer.from(line + "\r\n", 'binary'));
                } else {
                    fields[fields.length - 1].value += line;
                }
            }
        }

        if (isLast) {
            console.log(fields);
        }
    });

It works except that uploaded images are corrupted and are cut in randomly (Not the same in every image, some are totally corrupted and some are perfectly fine). Could someone point out what is wrong with it?

Thanks in advance :)


Solution

  • same situation here, i am using uwebsocket.js for uploading mp4 file, i try your code to upload mp4, it's uploaded but cannot be play. then i try to compare original file with uploaded file, the file size has change a litle bit.

    then i realize in your code :

    streams[streams.length - 1].write(Buffer.from(line + "\r\n", 'binary'));
    

    when split by "\r\n" last of array should not have "\r\n" than i add some condition below :

    if (i==buff.length-1)
       streams[streams.length - 1].write(Buffer.from(line, 'binary'));
    else {
       if (i+1<buff.length) {
          if (buff[i+1]==boundary + '--')
             streams[streams.length - 1].write(Buffer.from(line, 'binary'));
          else
             streams[streams.length - 1].write(Buffer.from(line + "\r\n", 'binary'));
       }
    }
    

    and now my uploaded mp4 play well.

    thanks....

    sorry for my bad english