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 }
原文地址:https://www.cnblogs.com/wyb666/p/9694481.html