【问题标题】:Node.js file upload server without third party module没有第三方模块的Node.js文件上传服务器
【发布时间】:2018-03-19 15:19:06
【问题描述】:

我想解析上传文件并在没有任何第三个模块的情况下保存但仍然没有成功。 我错过了任何部分吗?

还是需要先转成buffer?

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

【问题讨论】:

    标签: node.js file-upload multipartform-data


    【解决方案1】:

    参考:Form submission

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

    首先,dataBuffer。您不能直接使用+ 运算符。也正因为如此,你不能使用正则表达式。

    你可以试试这个

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

    其中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.");
    }
    

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

    【讨论】:

      【解决方案2】:

      上传的问题是服务器收到的数据不是同步的,而是块的。因此,如果您在接收到第一个块后保存数据,则会丢失其余数据。因此,您必须创建一个流并在流完成后创建文件。我用原始 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}`));
      

      将接收到的块存储在 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);
        });
      }
      

      只是一个简单的 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>`;
      }
      

      将所有三个部分写入一个文件并使用节点启动文件。

      【讨论】:

        猜你喜欢
        • 2015-02-26
        • 2015-07-08
        • 1970-01-01
        • 1970-01-01
        • 2013-04-26
        • 1970-01-01
        • 2020-01-17
        • 1970-01-01
        相关资源
        最近更新 更多