前言

在Web开发中,文件上传是一个非常常见、非常重要的功能。本文将介绍如何用Node处理上传的文件。

需求分析

由于现在前后端分离很流行,那么本文也直接采用前后端分离的做法。前端界面如下:

用户从浏览器中选择文件,点击上传,将发起http请求到服务器,服务器将接受到的文件存储在服务器硬盘中。

前端部分

ajax请求库采用axios,为了简化说明,前端限制上传的文件类型只能为图片,且一次只能上传一张,有兴趣的朋友可以自行补充,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<input type="file" name="file" accept="image/*" onchange="changeImg(event)"/>
<button onclick="submit()">上传</button> <script>
let file = ''
let fileName = '' function submit() {
let data = new FormData()
data.append('imgName', fileName)
data.append('img', file) axios({
method: 'post',
timeout: 2000,
url: 'http://localhost:3000/postApi',
data: data
})
.then(response => {
console.log(response.data)
})
.catch(error => {
console.log(error)
})
} function changeImg(e) {
file = e.target.files.item(0)
// 如果不选择图片
if (file === null) {
return
}
fileName = file.name
}
</script>
</body>
</html>

后端部分

这是本文要介绍的重点,为了用高效流畅的方式来解析文件上传请求,我们先引入formidable库:

npm install formidable --save

formidable的流式解析器让它成为了处理文件上传的绝佳选择,也就是说它能随着数据块的上传接收它们,解析它们,并吐出特定的部分,相信熟悉流的朋友会很好理解。这种方式不仅快,还不会因为需要大量缓冲而导致内存膨胀,即便像视频这种大型文件,也不会把进程压垮。
首先,我们在根目录下创建myImage文件,用于存放上传的图片(注意:如果没有创建,会导致上传报错),接着,我们创建一个IncomingForm实例form,并且设置存放路径为myImage文件夹。代码如下:

var http = require('http')
var formidable = require('formidable') var server = http.createServer(function(req, res){
// 1 设置cors跨域
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
res.setHeader('Content-Type', 'application/json') // 2
switch (req.method) {
case 'OPTIONS':
res.statusCode = 200
res.end()
break
case 'POST':
upload(req, res)
break
}
}) function upload(req, res) {
// 1 判断
if (!isFormData(req)) {
res.statusCode = 400
res.end('错误的请求, 请用multipart/form-data格式')
return
} // 2 处理
var form = new formidable.IncomingForm()
form.uploadDir = './myImage'
form.keepExtensions = true form.on('field', (field, value) => {
console.log(field)
console.log(value)
})
form.on('end', () => {
res.end('上传完成!')
}) form.parse(req)
} function isFormData(req) {
let type = req.headers['content-type'] || ''
return type.includes('multipart/form-data')
} server.listen(3000)
console.log('port is on 3000.')

node app开启http服务器后,在前端页面中上传一张kitty.jpg,我们看到控制台打印出了前端上传的imgName属性:kitty.jpg

并且,myImage文件夹目录下多了一张图片:

打开一看,正是从前端上传的那张kitty.jpg

文件改名

我们发现,这个默认的文件名称并不是我们想要的,我们想改成以当前时间戳命名的文件,添加的功能代码如下:

  var fs = require('fs')

  form.on('file', (name, file) => {
// 重命名文件
let types = file.name.split('.')
let suffix = types[types.length - 1]
fs.renameSync(file.path, './myImage/' + new Date().getTime() + '.' + suffix)
})

再次上传,发现现在存的照片名称已经变成我们想要的格式了。

添加上传进度

Formidable的progress事件能给出收到的字节数,以及期望收到的字节数。我们可以借助这个做出一个进度条。
我们为上面的程序添加下面的代码,每次有progress事件激发,就会计算百分比并用console.log()输出:

  form.on('progress', (bytesReceived, bytesExpected) => {
var percent = Math.floor(bytesReceived / bytesExpected * 100)
console.log(percent)
})

再次上传一张图片,现在控制台已经会打印出进度显示了:

当然,一般情况下,我们是要把这个进度传回到用户的浏览器中去,这对于任何想要上传大型文件的程序来说是个很棒的特性,并且这是个很适合用Node完成的任务。比如说用WebSocket协议,或者像Socket.IO这样的实时模块,关于Node中使用websocket,后面我会单独出一篇文章来介绍。

错误处理

任何时候都不要忘了对程序添加错误处理,如果你的程序在重要的时候崩掉了,可能轻则被老板打屁股,重则拉出去祭天。想象一下,如果用户上传的图片很大,并且用户的网络还很慢,那么上传的时间会超出前端代码中设置的请求超时时间2s,服务器就会崩掉,不信?我们来试一下。
首先,我选择了一张很大的图片,5M,并且用chrome浏览器将浏览器网络环境设置为slow 3g,设置方法如下:
f12打开开发者工具,在more tools--network conditions

点击上传,我们看见服务端控制台的信息如下,服务器崩掉了:

所以,最后我们加上了错误处理,代码如下:

  // 加上错误处理,防止用户网络慢,或者取消上传,导致服务器崩掉
form.on('error', err => {
console.log(err)
res.statusCode = 500
res.end('服务器内部错误!')
})

小结

现在,相信你已经学会了如何用Node处理文件上传了,结合前面的那篇用Node提供静态文件服务的文章,你是不是能够自己摸索着去尝试做一些有趣的事情了呢?

参考阅读

formidable文档
input的file类型的accept属性

本文转载于:猿2048https://www.mk2048.com/blog/blog.php?id=hbiij2jbh2j

用Node处理文件上传的更多相关文章

  1. Node.js 文件上传 cli tools

    Node.js 文件上传 cli tools byte stream 断点续传 refs xgqfrms 2012-2020 www.cnblogs.com 发布文章使用:只允许注册用户才可以访问!

  2. Node开发文件上传系统及向七牛云存储和亚马逊AWS S3的文件上传

    背景起,有奏乐: 有伟人曰:学习技能的最好途径莫过于理论与实践相结合. 初学Node这货时,每每读教程必会Fall asleep. 当真要开发系统时,顿觉精神百倍,即便踩坑无数也不失斗志. 因为同团队 ...

  3. Node.js文件上传

    Node.js express使用Multer实现文件上传html部分 <div> <h3>文件上传:</h3> 选择一个文件上传: <br/> < ...

  4. node+express实现文件上传功能

    在进行node web开发时,我们可能经常遇到上传文件的问题,这一块如果我们没有经验,可能会遇到很多坑,下面我将跟大家分享一下,实现文件上传的一些方式. 一.node+express文件上传的常用方式 ...

  5. NodeJS+formidable实现文件上传加自动重命名

    前述 本人node初学者,此前使用原生node实现文件上传时遇到了一些困难,只做到了.txt 和.png两中格式的文件可以正常上传,如果上传其他格式文件服务端保存的文件会无法正常打开,原因是对form ...

  6. node.js 在 Express4.0 框架使用 Connect-Busboy 实现文件上传

    node.js下四种post提交数据的方式 今天说分享的是其中一种,就是上传文件. Express 4.0 以后,将功能原子化,高内聚,低耦合,独立出了很多中间件 今天主要分享文件上传 对于conne ...

  7. node应用通过multer模块实现文件上传

    multer用于处理文件上传的nodejs中间件,主要跟express框架搭配使用,只支持表单MIME编码为multipart/form-data类型的数据请求. 如果要处理其他编码的表单数据可以通过 ...

  8. Node.js新手教程——怎样实现文件上传功能

    作者:zhanhailiang 日期:2014-11-16 本文将介绍怎样使用Node.js实现文件上传功能. 1. 初始化项目信息:npm init [root@~/wade/nodejs/node ...

  9. node.js系列(实例):原生node.js+formidable模块实现简单的文件上传

    /** * 原生node.js结合formidable模块实现图片上传改名 * @Author:Ghost * @Date:2016/07/15 * @description: * 1.引入模块htt ...

随机推荐

  1. Django的orm(一)

    Django的orm一 1.创建表 1.1 创建普通表 class UserType(models.Model): ''' 用户类型 ''' title=models.CharField(max_le ...

  2. 五、Java控制流程

    Java流程控制* 用户交互Scanner.Scanner进阶使用 用户交互Scanner ​ 之前我们学习的基本语法中我们并没有实现程序和人的交互,但是Java给我们提供了这样一个工具类,我们可以获 ...

  3. LeetCode-040-组合总和 II

    组合总和 II 题目描述:给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合. candidates 中的每个数字 ...

  4. 关于Union和 Union all,以及出现 ORA-12704:字符集不匹配问题

    一.Union和 Union all 1.Union 对两个结果集进行并集操作: 对结果进行去重操作,不包括重复行: 并进行默认排序. -----效率相对较低 2.Union all 对两个结果集进行 ...

  5. 『现学现忘』Docker基础 — 13、通过脚本安装Docker

    Docker官方提供方便用户操作的安装脚本,用起来是非常方便.但是要注意的是,使用脚本安装Docker,是安装最新版本的Docker. 注意:不建议在生产环境中使用安装脚本.因为在生产环境中一定不要最 ...

  6. php 23种设计模型 - 桥接模式

    桥接模式(Bridge) 桥接(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化.这种类型的设计模式属于结构型模式,它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦. 这种模式涉 ...

  7. PhpStorm中绘画UML

    IDE支持 在Plugins中 安装PlantUML integration插件 到http://www.graphviz.org/网站下载graphviz.exe并安装(这个软件可以支持更多的UML ...

  8. 解决 Vue 项目 invalid host header 问题(两种方案)

    问题出现背景 做微信H5网页时,使用花生壳内网穿透进行调试时,打开网页显示:invalid host header 分析问题 这句话的意思是:无效的Host请求头: 因为在vue在调试时相当于启动了一 ...

  9. vant中tab标签切换时会改变内容滚动高度

    vant的tabs标签页,标签切换时会改变内容区的滚动高度,这是因为内容区共用同一个父元素为滚动区域引起的,解决办法:在tabs的内容区域嵌套一层滚动区域,让每个内容区域使用单独的滚动元素就行了.   ...

  10. contos 配置国内yum源

    contos配置国内yum源 前言 rpm管理软件包的命令,很难用,需要手动解决以来关系,所以最好用 yum 的理念是使用一个中心仓库(repository)管理一部分甚至一个distribution ...