nodejs -- 用流实现上传文件

 内容:
1.文件上传基础
2.node文件处理机制
3.用流实现文件上传

1.文件上传基础
前端代码:
 <form action="localhost:8080/" method="post" enctype="multipart/form-data">
     <input type="text" name="user">
     <input type="password" name="pass">
     <input type="file" name="f1">
     <input type="submit" value="上传文件">
 </form>
 注意:
     上传文件时表单中的enctype="multipart/form-data"必须要写
     input(file)必须要有name

后端代码:
  const http = require('http');
  const uuid = require('uuid/v4');
  const fs = require('fs')
  let server_post = http.createServer((req, res) => {
      let arr = [];
  
      req.on('data', data => {
          arr.push(data);
     });
     req.on('end', () => {
         let data = Buffer.concat(arr);
         // console.log(data)
 
         //data
         //解析二进制文件上传数据
         let post = {};
         let files = {};
         if (req.headers['content-type']) {
             let str = req.headers['content-type'].split('; ')[1];
             if (str) {
                 let boundary = '--' + str.split('=')[1];
 
                 //1.用"分隔符切分整个数据"
                 let arr = (data.toString()).split(boundary);

                 //2.丢弃头尾两个数据
                 arr.shift();
                 arr.pop();
 
                 //3.丢弃掉每个数据头尾的"\r\n"
                 arr = arr.map(buffer => buffer.slice(2, buffer.length - 2));
 
                 //4.每个数据在第一个"\r\n\r\n"处切成两半
                 arr.forEach(buffer => {
                     let n = buffer.indexOf('\r\n\r\n');
 
                     let disposition = buffer.slice(0, n);
                     let content = buffer.slice(n + 4);
 
                     disposition = disposition.toString();
 
                     if (disposition.indexOf('\r\n') === -1) {
                         //普通数据
                         //Content-Disposition: form-data; name="user"
                         content = content.toString();
 
                         let name = disposition.split('; ')[1].split('=')[1];
                         name = name.substring(1, name.length - 1);
 
                         post[name] = content;
                     } else {
                         //文件数据
                         /*Content-Disposition: form-data; name="f1"; filename="a.txt"\r\n
                         Content-Type: text/plain*/
                         let [line1, line2] = disposition.split('\r\n');
                         let [, name, filename] = line1.split('; ');
                         let type = line2.split(': ')[1];
 
                         name = name.split('=')[1];
                         name = name.substring(1, name.length - 1);
                         filename = filename.split('=')[1];
                         filename = filename.substring(1, filename.length - 1);
 
                         let path = `upload/${uuid().replace(/\-/g, '')}`;
 
                         fs.writeFile(path, content, err => {
                             if (err) {
                                 console.log('文件写入失败', err);
                             } else {
                                 files[name] = {filename, path, type};
                                 console.log(files);
                             }
                         });
                     }
                 });
 
                 //5.完成
                 console.log(post);
             }
         }
 
         res.end();
     });
 });
 server_post.listen(8080);

2.node文件处理机制
node文件上传从根本上来说就两种方法:
(1)最基础原始的方法
使用fs中的readFile和writeFile实现(读取完上传的文件后保存)
这样做有弊端:
只能等到所有数据都到达了才开始处理
readFile先把所有数据全读到内存中,然后回调:
1.极其占用内存
2.资源利用极其不充分
(2)更好的方法
使用流,收到一部分数据就直接解析一部分,实例见后面的文件上传实例

3.用流实现文件上传
(1)流
三种流:
读取流  -->  fs.createReadStream、req
写入流  -->  fs.createWriteStream、res
读写流  -->  压缩、加密
(2)流实现读写文件

const fs = require('fs')
  
  let rs = fs.createReadStream('1.txt')       // 读取流
  let ws = fs.createWriteStream('2.txt')      // 写入流
  
  rs.pipe(ws)
  
  // 异常处理
  rs.on('error', function (error) {
     console.log('读取失败!')
 })
 
 // 读取完成 及 写入完成
 rs.on('end', function () {
     console.log('读取完成!')
 })
 
 ws.on('finish', function () {
     console.log('写入完成!')
 })
注:1.txt应该在同级目录下

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

  /**
   * [saveFileWithStream description]
   * @param {String} filePath [文件路径]
   * @param {Buffer} readData [Buffer 数据]
   */
  static saveFile(filePath, fileData) {
   return new Promise((resolve, reject) => {
    // 块方式写入文件
    const wstream = fs.createWriteStream(filePath);
  
   wstream.on('open', () => {
    const blockSize = 128;
    const nbBlocks = Math.ceil(fileData.length / (blockSize));
    for (let i = 0; i < nbBlocks; i += 1) {
     const currentBlock = fileData.slice(
      blockSize * i,
      Math.min(blockSize * (i + 1), fileData.length),
     );
     wstream.write(currentBlock);
    }
  
    wstream.end();
   });
   wstream.on('error', (err) => { reject(err); });
   wstream.on('finish', () => { resolve(true); });
  });
 }
 
 // 实际调用的时候,如下:
 try {
  await saveFileWithStream(filePath, fileData); // 这里的fileData是Buffer类型
 } catch (err) {
  console.log(err.stack);
 }

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它可以使JavaScript在服务器端运行。要实现文件上传和下载,可以使用Node.js的一些模块和框架来简化开发过程。 对于文件上传,可以使用Multer作为中间件进行数据处理。Multer是一个流行的Node.js中间件,用于处理表单数据,特别是文件上传。以下是一个简单的示例代码: ```javascript const express = require('express'); const multer = require('multer'); const app = express(); const upload = multer({ dest: 'uploads/' }); app.post('/upload', upload.single('file'), (req, res) => { // 处理上传的文件 console.log(req.file); res.send('文件上传成功!'); }); app.listen(8080, () => { console.log('服务器已启动,监听端口8080'); }); ``` 上述代码创建了一个基本的Express应用程序,并使用Multer中间件来处理文件上传。`upload.single('file')`表示只接受名为`file`的单个文件。在回调函数中可以进行文件处理的操作。 对于文件下载,可以使用Node.js的内置模块`fs`和`http`来实现。以下是一个简单的示例代码: ```javascript const http = require('http'); const fs = require('fs'); http.createServer((req, res) => { if (req.method === 'GET' && req.url === '/download') { const file = fs.createReadStream('path/to/file.txt'); res.setHeader('Content-disposition', 'attachment; filename=file.txt'); file.pipe(res); } }).listen(8080, () => { console.log('服务器已启动,监听端口8080'); }); ``` 上述代码创建了一个简单的HTTP服务器,当GET请求路径为`/download`时,会将文件作为附件下载。可以通过设置`Content-disposition`头来指定下载文件的名称。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值