简体   繁体   English

无第三方模块的Node.js文件上传服务器

[英]Node.js file upload server without third party module

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);

Ref: Form submission参考: 表单提交

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

First, the data is a Buffer .首先, data是一个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:其中onPostingonPosted定义如下:

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:还有 2 个函数可以帮助将表单数据(允许多个文件)解析为一个对象:

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;
}

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.我用原始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将接收到的块存储在 tmp 文件中并创建文件

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只是一个用于视口支持的简单 html 表单

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.将所有三个部分写入一个文件并使用 node.js 启动该文件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM