内容:

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.丢弃掉每个数据头尾的"\r\n"
  32. arr = arr.map(buffer => buffer.slice(2, buffer.length - 2));
  33.  
  34. //4.每个数据在第一个"\r\n\r\n"处切成两半
  35. arr.forEach(buffer => {
  36. let n = buffer.indexOf('\r\n\r\n');
  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('\r\n') === -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"\r\n
  55. Content-Type: text/plain*/
  56. let [line1, line2] = disposition.split('\r\n');
  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. //5.完成
  79. console.log(post);
  80. }
  81. }
  82.  
  83. res.end();
  84. });
  85. });
  86. 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. }

node进阶之用流实现上传文件的更多相关文章

  1. [Node.js] 使用File API 异步上传文件

    原文地址:http://www.moye.me/2014/11/05/html5-filereader/ 最近在做一个网盘的项目,不出意外的涉及到大文件的上传,那么问题来了:如何实时的显示文件上传的进 ...

  2. js上传文件获取文件流

    上传文件获取文件流 <div> 上传文件 : <input type="file" name = "file" id = "file ...

  3. 记录一次node中台转发表单上传文件到后台过程

    首发掘金 记录一次node中台转发表单上传文件到后台过程 本篇跟掘金为同一个作者leung   公司几个项目都是三层架构模式即前台,中台(中间层),后台.前台微信端公众号使用vue框架,后台管理前端使 ...

  4. PHP流式上传和表单上传(美图秀秀)

    最近需要开发一个头像上传的功能,找了很多都需要授权的,后来找到了美图秀秀,功能非常好用. <?php /** * Note:for octet-stream upload * 这个是流式上传PH ...

  5. Asp.net上传文件后台通过二进制流发送到其他Url保存

    实际情况一般有单独的站点存放静态文件,比如图片.office文档等.A站点的操作需要上传文件到B站点, 下面介绍一种方法通过System.Net.WebClient类的UploadData方法 . u ...

  6. 上传文件报错System.Net.ProtocolViolationException: 必须先将 ContentLength 字节写入请求流,然后再调用 [Begin]GetResponse。

    在上传文件的时候报错. 错误: System.Net.ProtocolViolationException: 必须先将 ContentLength 字节写入请求流,然后再调用 [Begin]GetRe ...

  7. Nodejs学习笔记(八)--- Node.js + Express 实现上传文件功能(felixge/node-formidable)

    目录 前言 formidable简介 创建项目并安装formidable 实现上传功能 运行结果 部分疑惑解析 写在之后 前言 前面讲了一个构建网站的示例,这次在此基础上再说说web的常规功能---- ...

  8. c#上传文件(二)使用文件流保存文件

    1.html代码: <asp:FileUpload runat="server" ID="UpLoadFile"/> <asp:Button ...

  9. IOS--工作总结--post上传文件(以流的方式上传)

    1.添加协议 <NSURLConnectionDelegate> 2.创建 @property (nonatomic,retain) NSURLConnection* aSynConnec ...

随机推荐

  1. eclipse ubuntu error

    eclipse cdt :symbol cout can not be solved Preferences -> C/C++ -> Indexer -> Use active bu ...

  2. easyui学习笔记10—手风琴格子始终展开和多个格子展开

    始终打开有时候可能会很管用,其实就是一个设置问题.这里就不再介绍引用的资源了,这里只看看html是怎么写的. 1.html代码 <body> <h2>Basic Accordi ...

  3. Mr. Kitayuta's Colorful Graph CodeForces - 506D(均摊复杂度)

    Mr. Kitayuta has just bought an undirected graph with n vertices and m edges. The vertices of the gr ...

  4. HDU2034:人见人爱A-B

    Problem Description 参加过上个月月赛的同学一定还记得其中的一个最简单的题目,就是{A}+{B},那个题目求的是两个集合的并集,今天我们这个A-B求的是两个集合的差,就是做集合的减法 ...

  5. Linux Shell查看物理CPU个数、核数、逻辑CPU个数

    Linux Shell常用命令: ====================================== # 总核数 = 物理CPU个数 X 每颗物理CPU的核数 # 总逻辑CPU数 = 物理C ...

  6. 有向图与无向图的合并操作区别D(递归与并查集)

    有向图的合并,典型问题:通知小弟(信息只能单向传播)https://www.nowcoder.com/acm/contest/76/E 无向图的合并,典型问题:修道路问题 由于无向图只要二者有联系即可 ...

  7. YUI JS压缩Ant脚本

    <?xml version="1.0" encoding="UTF-8"?><!-- 对指定目录下的所有js进行压缩,放入指定位置 --> ...

  8. statik golang 静态资源嵌入二进制文件工具使用(docker 构建)

      将静态资源打包进二进制文件有好多方便的地方 方便客户演示 代码简单加密 运行方便 statik 就是一款在golang 中用的比较多,nodejs 有一款pkg (oclif 就推荐使用此工具) ...

  9. Postman 常用测试结果验证及使用技巧

    Postman的test本质上是JavaScript代码,通过我们编写测试代码,每一个tests返回True,或是False.每一个tests实际上就是一个测试用例 官方文档给出了很多验证方式,我们通 ...

  10. spring答题

    ioc 依赖注入:通过注入的方式实例化对象,不再直接new对象了,交给spring容器进行管理和维护 控制反转:实例化对象的控制权交给了spring容器,而不再是某个单独的类,控制权发生了变更 作用: ...