2

I want to parse the upload file and saved without any 3rd module but still not success. Do I miss any part?

Or it need to convert to buffer first?

var http = require('http');
const fs = require ('fs');

http.createServer(function (req, res) {
  if (req.url == '/fileupload') {
    var body = '';
    req.on('data', (data) => {
      body += data;
    });
    req.on('end', () => {

      body = body.replace(/-.+-/g, '').replace(/WebKit.+|Contentdata.+|Content-Type.+/g, '');
      fs.writeFile('test.png', body, (err) => {
        if (err) throw err;
        console.log('The file has been saved!');
      });
      res.end(body);
    })
  } else {
    res.writeHead(200, { 'Content-Type': 'text/html' });
    res.write('<form action="fileupload" method="post" enctype="multipart/form-data">');
    res.write('<input type="file" name="filetoupload"><br>');
    res.write('<input type="submit">');
    res.write('</form>');
    return res.end();
  }
}).listen(8080);

2 Answers 2

2

Ref:Form submission

req.on('data', (data) => {
  body += data;
});

First, the data is a Buffer. You cannot use the + operator directly. And also because of this, you cannot use regular expression.

You may try this

req.res=res;
req.on("data", onPosting).on("end", onPosted);

where onPosting and onPosted are defined as below:

function onPosting(data){
  if (this.data){
      this.data.fill(data, this.dataIndex);
      this.dataIndex += data.length;
  } else { 
      var contentLength = +this.headers["content-length"];
      if (data.length === contentLength){
          this.data = data;
      } else {
          this.data = Buffer.alloc(contentLength);
          this.data.fill(data);
          this.dataIndex = data.length;
      }
  }
}

function onPosted(){
  var boundary = extract(this.headers["content-type"], " boundary=");
  var form = parseForm(boundary, this.data);
  console.log(form);
  this.res.end("Data Posted.");
}

And 2 more functions to help parsing the form-data (allow multiple files) to an object:

function extract(arr, start, end){
  var useIndex = typeof start === "number",
      i,
      j;
  if (useIndex){
      i = start;
     if (end){
          j = arr.indexOf(end, i);
          return (j === -1) ? ["", -1] : [ (i === j) ? "" : arr.slice(i, j), j + end.length];
      } else return arr.slice(i);
  } else {
      i = arr.indexOf(start);
      if (i !== -1){
          i += start.length;
          if (end){
              j = arr.indexOf(end, i);
              if (j !== -1) return arr.slice(i, j);
          } else return arr.slice(i);
      }
      return "";
  }
}

function parseForm(boundary, data){
  var form = {},
      delimiter = Buffer.from("\r\n--" + boundary),
      body = extract(data, "--" + boundary + "\r\n"),
      CR = Buffer.from("\r\n\r\n"),
      i = 0,
      head,
      name,
      filename,
      value,
      obj;
  if (body) {
      while (i !== -1){
          [head, i] = extract(body, i, CR);
          name = extract(head, '; name="', '"').toString();
          filename = extract(head, '; filename="', '"').toString();
          [value, i] = extract(body, i, delimiter);
          if (name){
              obj = filename ? {filename, value} : {value};
              if (form.hasOwnProperty(name)){ // multiple 
                  if (Array.isArray(form[name])){
                      form[name].push(obj);
                  } else {
                      form[name] = [form[name], obj];
                  }
              } else {
                  form[name] = obj;
              }
          }
          if (body[i] === 45 && body[i + 1] === 45) break; // "--"
          if (body[i] === 13 && body[i + 1] === 10){
              i += 2; // "\r\n"
          } else {
              //error
          }
      }
  }
  return form;
}
Sign up to request clarification or add additional context in comments.

Comments

2

The problem with upload is that the data recieved by servers is not sync but chunk. So if you save the data after receiving the first chunk, you loose the rest of the data. So instead, you have to create a stream and create the file after the stream is complete. I made one using raw js.

const http = require('http'),
      port = process.env.PORT || 9000,
      host = process.env.HOST || '127.0.0.1';
//tested on node=v10.19.0, export HOST="192.168.0.103"
http.createServer(function(req, res) {

  // Check if form is submitted and save its content
  if (req.method == "POST") try {
    store_file(req);

  // This is here incase any errors occur
  } catch (error) {
    res.writeHead(404, {"content-type":"text/plain; charset=utf-8"});
    res.end("Server Borked");

    // error is object but response.write require string/buffer
    console.dir(error);
    return;
  }

  // respond with a simple html form so they can post more data
  res.writeHead(200, {"content-type":"text/html; charset=utf-8"});
  res.end(simple_html_form());
}).listen(port, host, () => console.dir(`Serving at http://${host}:${port}`));

Store received chunks in tmp file and create file

const fs = require('fs'),
      os = require('os'),
      path = require('path');

function store_file(req) {
  // Resolve path/to/temp/file
  var temp = path.resolve(os.tmpdir(), 'temp' + Math.floor(Math.random() * 10));

  // This opens up the writeable stream to temporary file
  var writeStream = fs.createWriteStream(temp);

  // Write data in memory instead of storage
  //writeStream.cork(); // disabled for causing hang

  // This pipes the POST data to the file
  req.pipe(writeStream);

  // After the temporary file is creates, create real file
  writeStream.on('finish', () => {

    reader = fs.readFileSync(temp);
    filename = reader.slice(reader.indexOf("filename=\"") + "filename=\"".length, reader.indexOf("\"\r\nContent-Type"));
    boundary = reader.slice(0,reader.indexOf('\r\n'));
    content = reader.slice(reader.indexOf('\r\n\r\n') + '\r\n\r\n'.length, reader.lastIndexOf(Buffer.from('\r\n') + boundary));

    // After real file is created, delete temporary file
    fs.writeFileSync(filename.toString(), content);
    fs.unlinkSync(temp);
  });
}

Just a simple html form for viewport support

function simple_html_form() {
    return `
<!DOCTYPE html>
<html lang="en">
<head>
  <meta http-equiv="content-type" content="text/html" charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Upload</title>
</head>
<body>
<form  method="post" enctype="multipart/form-data">
  <input type="file" name="fileUpload">
  <input type="submit" value="Upload">
</form>
</body>
</html>`;
}

write all three sections in one file and start the file using node.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.