zoukankan      html  css  js  c++  java
  • node进阶之用流实现上传文件

    内容:

    1.文件上传基础

    2.node文件处理机制

    3.用流实现文件上传

    1.文件上传基础

    前端代码:

    1 <form action="localhost:8080/" method="post" enctype="multipart/form-data">
    2     <input type="file" name="f1">
    3     <input type="submit" value="上传文件">
    4 </form>
    5 
    6 注意:
    7     上传文件时表单中的enctype="multipart/form-data"必须要写
    8     input(file)必须要有name

    后端代码:

     1 const http = require('http');
     2 const uuid = require('uuid/v4');
     3 const fs = require('fs')
     4 
     5 let server_post = http.createServer((req, res) => {
     6     let arr = [];
     7 
     8     req.on('data', data => {
     9         arr.push(data);
    10     });
    11     req.on('end', () => {
    12         let data = Buffer.concat(arr);
    13         // console.log(data)
    14 
    15         //data
    16         //解析二进制文件上传数据
    17         let post = {};
    18         let files = {};
    19         if (req.headers['content-type']) {
    20             let str = req.headers['content-type'].split('; ')[1];
    21             if (str) {
    22                 let boundary = '--' + str.split('=')[1];
    23 
    24                 //1.用"分隔符切分整个数据"
    25                 let arr = (data.toString()).split(boundary);
    26 
    27                 //2.丢弃头尾两个数据
    28                 arr.shift();
    29                 arr.pop();
    30 
    31                 //3.丢弃掉每个数据头尾的"
    "
    32                 arr = arr.map(buffer => buffer.slice(2, buffer.length - 2));
    33 
    34                 //4.每个数据在第一个"
    
    "处切成两半
    35                 arr.forEach(buffer => {
    36                     let n = buffer.indexOf('
    
    ');
    37 
    38                     let disposition = buffer.slice(0, n);
    39                     let content = buffer.slice(n + 4);
    40 
    41                     disposition = disposition.toString();
    42 
    43                     if (disposition.indexOf('
    ') === -1) {
    44                         //普通数据
    45                         //Content-Disposition: form-data; name="user"
    46                         content = content.toString();
    47 
    48                         let name = disposition.split('; ')[1].split('=')[1];
    49                         name = name.substring(1, name.length - 1);
    50 
    51                         post[name] = content;
    52                     } else {
    53                         //文件数据
    54                         /*Content-Disposition: form-data; name="f1"; filename="a.txt"
    
    55                         Content-Type: text/plain*/
    56                         let [line1, line2] = disposition.split('
    ');
    57                         let [, name, filename] = line1.split('; ');
    58                         let type = line2.split(': ')[1];
    59 
    60                         name = name.split('=')[1];
    61                         name = name.substring(1, name.length - 1);
    62                         filename = filename.split('=')[1];
    63                         filename = filename.substring(1, filename.length - 1);
    64 
    65                         let path = `upload/${uuid().replace(/-/g, '')}`;
    66 
    67                         fs.writeFile(path, content, err => {
    68                             if (err) {
    69                                 console.log('文件写入失败', err);
    70                             } else {
    71                                 files[name] = {filename, path, type};
    72                                 console.log(files);
    73                             }
    74                         });
    75                     }
    76                 });
    77 
    78 
    79                 //5.完成
    80                 console.log(post);
    81             }
    82         }
    83 
    84 
    85         res.end();
    86     });
    87 });
    88 server_post.listen(8080);

    2.node文件处理机制

    node文件上传从根本上来说就两种方法:

    (1)最基础原始的方法

    使用fs中的readFile和writeFile实现(读取完上传的文件后保存)

    这样做有弊端:

    • 只能等到所有数据都到达了才开始处理
    • readFile先把所有数据全读到内存中,然后回调:
    • 1.极其占用内存
    • 2.资源利用极其不充分

    (2)更好的方法

    使用流,收到一部分数据就直接解析一部分,实例见后面的文件上传实例

    3.用流实现文件上传

    (1)流

    三种流:

    • 读取流  -->  fs.createReadStream、req
    • 写入流  -->  fs.createWriteStream、res
    • 读写流  -->  压缩、加密

    (2)流实现读写文件

     1 const fs = require('fs')
     2 
     3 let rs = fs.createReadStream('1.txt')       // 读取流
     4 let ws = fs.createWriteStream('2.txt')      // 写入流
     5 
     6 rs.pipe(ws)
     7 
     8 // 异常处理
     9 rs.on('error', function (error) {
    10     console.log('读取失败!')
    11 })
    12 
    13 // 读取完成 及 写入完成
    14 rs.on('end', function () {
    15     console.log('读取完成!')
    16 })
    17 
    18 ws.on('finish', function () {
    19     console.log('写入完成!')
    20 })

    注:1.txt应该在同级目录下

    (3)用流实现上传文件核心代码

     1 /**
     2  * [saveFileWithStream description]
     3  * @param {String} filePath [文件路径]
     4  * @param {Buffer} readData [Buffer 数据]
     5  */
     6 static saveFile(filePath, fileData) {
     7  return new Promise((resolve, reject) => {
     8   // 块方式写入文件
     9   const wstream = fs.createWriteStream(filePath);
    10  
    11   wstream.on('open', () => {
    12    const blockSize = 128;
    13    const nbBlocks = Math.ceil(fileData.length / (blockSize));
    14    for (let i = 0; i < nbBlocks; i += 1) {
    15     const currentBlock = fileData.slice(
    16      blockSize * i,
    17      Math.min(blockSize * (i + 1), fileData.length),
    18     );
    19     wstream.write(currentBlock);
    20    }
    21  
    22    wstream.end();
    23   });
    24   wstream.on('error', (err) => { reject(err); });
    25   wstream.on('finish', () => { resolve(true); });
    26  });
    27 }
    28 
    29 // 实际调用的时候,如下:
    30 try {
    31  await saveFileWithStream(filePath, fileData); // 这里的fileData是Buffer类型
    32 } catch (err) {
    33  console.log(err.stack);
    34 }
  • 相关阅读:
    南阳理工ACM(题目56)
    南阳理工ACM(题目56)
    南阳理工ACM(题目56)
    csuoj1009
    素数槽csuoj
    简单动态规划问题分析
    sort函数使用的基本知识
    2014年7月19日——比赛题取石头问题1
    CODEVS——T 1269 匈牙利游戏 2012年CCC加拿大高中生信息学奥赛
    洛谷—— P1640 [SCOI2010]连续攻击游戏
  • 原文地址:https://www.cnblogs.com/wyb666/p/9694481.html
Copyright © 2011-2022 走看看