如何在Node.js和Express中上传文件

大量的移动应用程序和网站允许用户上传个人资料图片和其他文件。 因此,在使用Node.js和Express构建REST API时,通常需要处理文件上传。

在本教程中,我们将讨论如何使用Node.js和Express后端处理单个和多个文件上传,以及如何将上传的文件保存在服务器上。

安装

首先,让我们通过运行以下命令来创建一个新的Node.js应用程序。 我正在使用npm进行软件包管理。 如果愿意,可以使用yarn

# creat a new directory & switch to it
$ mkdir files-upload && cd files-upload

# run this for npm to create a new app
$ npm init -y

# run this for yarn to create a new app
$ yarn init -y 

-y--yes跳过交互会话,并根据默认设置生成package.json文件。 接下来,运行以下命令以安装必需的依赖项:

# run this for npm
$ npm install express body-parser cors express-fileupload morgan lodash --save

# or using yarn
$ yarn add express body-parser cors express-fileupload morgan lodash 

以下是上述每个软件包的功能:

  • express-基于Node.js构建的流行Web框架。 我们将使用它来开发REST API。
  • body-parser-Node.js请求主体解析中间件,该中间件在处理程序之前解析传入的请求主体,并使其在req.body属性下可用。 简而言之,它简化了传入请求。
  • cors-另一种Express中间件,用于启用CORS(跨域资源共享)请求。
  • express-fileupload-用于上传文件的Simple Express中间件。 它解析multipart/form-data请求,提取文件(如果有),并在req.files属性下使它们可用。
  • morgan-用于记录HTTP请求的Node.js中间件。
  • lodash-一个JavaScript库,为数组,数字,对象,字符串等提供实用程序功能。

创建Express服务器

安装所需的依赖项之后,让我们开始创建Express服务器。
index.js

const express = require('express');
const fileUpload = require('express-fileupload');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const _ = require('lodash');

const app = express();

// enable files upload
app.use(fileUpload({
    createParentPath: true
}));

//add other middleware
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(morgan('dev'));

//start app 
const port = process.env.PORT || 3000;

app.listen(port, () => 
  console.log(`App is listening on port ${port}.`)
);

上面的代码很容易理解。 它首先设置express-fileupload中间件以启用multipart/form-data请求。 之后,添加了其他Express中间件以允许跨域资源共享(CORS),请求正文解析和HTTP请求日志记录。 最后,它将在端口3000上启动服务器。

上传单个文件

让我们创建第一个路由,该路由允许用户上传其个人资料图片。

app.post('/upload-avatar', async (req, res) => {
    try {
        if(!req.files) {
            res.send({
                status: false,
                message: 'No file uploaded'
            });
        } else {
            //Use the name of the input field (i.e. "avatar") to retrieve the uploaded file
            let avatar = req.files.avatar;
            
            //Use the mv() method to place the file in upload directory (i.e. "uploads")
            avatar.mv('./uploads/' + avatar.name);

            //send response
            res.send({
                status: true,
                message: 'File is uploaded',
                data: {
                    name: avatar.name,
                    mimetype: avatar.mimetype,
                    size: avatar.size
                }
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

上面的代码段是一个HTTP POST函数。 当您向/upload-avatar路由发送multipart/form-data请求以上传文件时,此功能会将文件保存到服务器上的uploads文件夹中。

express-fileupload中间件如何工作?

它使上传的文件可从req.files属性访问。 例如,如果您上传名为my-profile.jpg的文件,并且您的字段名是avatar,则可以通过req.files.avatar访问它。 avatar对象将包含以下信息:

  • avatar.name-上传文件的名称,即my-profile.jpg
  • avatar.mv-将文件移动到服务器上其他位置的功能
  • avatar.mimetype-文件的mime类型
  • avatar.size-文件大小,以字节为单位
  • avatar.data-上载文件的缓冲区表示

上传多个文件

让我们开始创建另一条路由,以允许用户一次上传多张照片。

app.post('/upload-photos', async (req, res) => {
    try {
        if(!req.files) {
            res.send({
                status: false,
                message: 'No file uploaded'
            });
        } else {
            let data = []; 
    
            //loop all files
            _.forEach(_.keysIn(req.files.photos), (key) => {
                let photo = req.files.photos[key];
                
                //move photo to uploads directory
                photo.mv('./uploads/' + photo.name);

                //push file details
                data.push({
                    name: photo.name,
                    mimetype: photo.mimetype,
                    size: photo.size
                });
            });
    
            //return response
            res.send({
                status: true,
                message: 'Files are uploaded',
                data: data
            });
        }
    } catch (err) {
        res.status(500).send(err);
    }
});

上面的代码与单文件上传非常相似,除了我们现在收到的是photos字段而不是avatar。 我们使用lodash实用程序函数(_.forEach()_.keysIn())遍历photos字段,然后将每张照片保存到uploads目录。

测试应用程序

我们快完成了! 在终端的项目根目录中运行以下命令以启动应用程序:

$ node index.js

它将在端口3000上启动应用程序。让我们使用Postman发送HTTP multipart/form-data请求:

1. 单文件

Single File

2. 多个文件

多个文件

如果您想使上传的文件可以从任何地方公开访问,只需将uploads目录设置为静态:

app.use(express.static('uploads'));

现在,您可以直接在浏览器中打开上传的文件:

http://localhost:3000/icon.png

文件大小限制

如果要限制一次上传的文件的大小,请将limits选项直接传递给express-fileupload中间件:

app.use(fileUpload({
    createParentPath: true,
    limits: { 
        fileSize: 2 * 1024 * 1024 * 1024 //2MB max file(s) size
    },
}));

源代码:在MIT许可下从GitHub下载完整的源代码。

结论

那是所有! 我们学习了如何使用Node.js和Express框架上传单个和多个文件。 ·express-fileupload·是一种易于使用的Express中间件,用于处理文件上传。 查看其文档以获取更多配置选项。

如果您有任何疑问或反馈,请随时给我发送tweet

快乐学习Node.js😍

阅读下一篇:使用Node.js中的Multer快速上传文件

相关博客链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348