使用Multer进行文件上传

File upload is a common feature that almost every website needs. We will go through step by step on how to handle single and multiple file(s) upload with Express, save it to database (LokiJs), and retrieve the saved file for viewing.

文件上传是几乎每个网站都需要的常见功能。 我们将逐步介绍如何使用Express处理单个和多个文件上传,将其保存到数据库(LokiJs),以及检索保存的文件以进行查看。

The complete sourcecode is available here: https://github.com/chybie/file-upload-express.

完整的源代码可以在这里找到: https : //github.com/chybie/file-upload-express

We will be using Typescript throughout this tutorial.

在整个教程中,我们将使用Typescript。

安装必需的依赖项 ( Install Required Dependencies )

I am using Yarn for package management. However, you can use npm if you like.

我正在使用Yarn进行程序包管理。 但是,您可以根据需要使用npm

依存关系 (Dependencies)

Run this command to install required dependencies

运行此命令以安装必需的依赖项

// run thisfor yarn
yarn add express cors multer lokijs del

// or using npm
npm install express cors multer lokijs del --save

Notes:-

笔记:-

  • express: We will develop our API using ExpressJs

    express :我们将使用ExpressJs开发我们的API
  • cors: A node.js package that provides an Express/Connect middleware to enable Cross Origin Resource Sharing (CORS)

    cors :一个node.js软件包,提供Express / Connect中间件以启用跨源资源共享(CORS)
  • multer: Node.js middleware for handling multipart/form-data.

    multer :用于处理multipart/form-data Node.js中间件。
  • loki: LokiJs, a fast, in-memory document-oriented datastore for node.js, browser and cordova

    loki :LokiJs,一种用于node.js,浏览器和cordova的快速的,面向内存的,面向文档的数据存储
  • del: Delete files and folders

    del :删除文件和文件夹
开发依赖 (Development Dependencies)

Since we are using Typescript, we need to install typings files in order to have auto-complete function (intellesense) during development.

由于我们使用的是Typescript,因此我们需要安装打字文件,以便在开发过程中具有自动完成功能(intellesense)。

// run thisfor yarn
yarn add typescript @types/express @types/multer @types/lokijs @types/del --dev

// or using npm
npm install typescript @types/express @types/multer @types/lokijs @types/del --save-dev

建立 ( Setup )

A couple of setup steps to go before we start.

在开始之前,需要执行几个设置步骤。

打字稿配置 (Typescript Configuration)

Add a typescript configuration file. To know more about Typescript configuration, visit https://www.typescriptlang.org/docs/handbook/tsconfig-json.html.

添加一个打字稿配置文件。 要了解有关Typescript配置的更多信息,请访问https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

// tsconfig.json{
    "compilerOptions": {
        "module": "commonjs",
        "moduleResolution": "node",
        "target": "es6",
        "noImplicitAny": false,
        "sourceMap": true,
        "outDir": "dist"
    }
}

Notes:-

笔记:-

  1. The compiled javascript code will be output to dist folder.

    编译后的javascript代码将输出到dist文件夹。
  2. Since Node Js 7.5+ support ES6 / 2015, we will set the target as es6.

    由于Node Js 7.5+支持ES6 / 2015,因此我们将target设置为es6
启动脚本 (Start Script)

Add the following scripts.

添加以下脚本。

// package.json{
    ...
    "scripts": {
        "prestart": "tsc",
        "start": "node dist/index.js"
    }
    ...
}

Later on we can run yarn start or npm start to start our application.

稍后,我们可以运行yarn startnpm start来启动我们的应用程序。

Notes:-

笔记:-

  1. When we run yarn start, it will trigger prestart script first. The command tsc will read the tsconfig.json file and compile all typescript files to javascript in dist folder.

    当我们运行yarn start ,它将首先触发prestart yarn start脚本。 tsc命令将读取tsconfig.json文件,并将所有typescript文件编译为dist文件夹中的javascript
  2. Then, we will run the compiled index file dist/index.js.

    然后,我们将运行已编译的索引文件dist/index.js

启动Express Server ( Starting Express Server )

Let's start creating our Express server.

让我们开始创建Express服务器。

// index.ts

import * as express from 'express'
import * as multer from 'multer'
import * as cors from 'cors'
import * as fs from 'fs'
import * as path from 'path'
import * as Loki from 'lokijs'

// setup
const DB_NAME = 'db.json';
const COLLECTION_NAME = 'images';
const UPLOAD_PATH = 'uploads';
const upload = multer({ dest: `${UPLOAD_PATH}/` }); // multer configuration
const db = new Loki(`${UPLOAD_PATH}/${DB_NAME}`, { persistenceMethod: 'fs' });

// app
const app = express();
app.use(cors());

app.listen(3000, function () {
    console.log('listening on port 3000!');
});

The code is pretty expressive itself. We allow Cross-Origin Resource Sharing (CORS), set the connection port to 3000, and start the server.

该代码本身具有很好的表达能力。 我们允许跨域资源共享(CORS),将连接端口设置为3000 ,然后启动服务器。

上传单个文件 ( Upload Single File )

Let's create our first route. We will create a route to allow users to upload their profile avatar.

让我们创建第一个路线。 我们将创建一条路径,以允许用户上传其个人资料头像。

路线 (Route)
// index.ts
...
import {
    loadCollection
} from './utils';
...

app.post('/profile', upload.single('avatar'), async (req, res) => {
    try {
        const col = await loadCollection(COLLECTION_NAME, db);
        const data = col.insert(req.file);

        db.saveDatabase();
        res.send({ id: data.$loki, fileName: data.filename, originalName: data.originalname });
    } catch (err) {
        res.sendStatus(400);
    }
})

Notes:

笔记:

  1. This is a HTTP POST function.

    这是一个HTTP POST函数。
  2. upload.single('avatar') is Multer middleware. It means we accept a single file with the field name avatar. File upload will be handled by Multer.

    upload.single('avatar')是Multer中间件。 这意味着我们接受字段名称为avatar的单个文件。 文件上传将由Multer处理。
  3. Multer will add a file property for request when it's a single file upload.

    当单个文件上传时,Multer会添加file属性以供request
  4. We will then load the LokiJs collection/table(we will create this function next), and insert the request file req.file to the collection.

    然后,我们将加载LokiJs集合/表(接下来将创建此函数),并将请求文件req.file插入到集合中。
加载LokiJs集合 (Load LokiJs Collection)

A generic function to retrieve a LokiJs collection if exists, or create a new one if it doesn't.

一种通用函数,用于检索LokiJs集合(如果存在),或者创建一个新的(如果不存在)。

// utils.ts

import * as del from 'del';
import * as Loki from 'lokijs';

const loadCollection = function (colName, db: Loki): Promise<LokiCollection<any>> {
    return new Promise(resolve => {
        db.loadDatabase({}, () => {
            const _collection = db.getCollection(colName) || db.addCollection(colName);
            resolve(_collection);
        })
    });
}

export { loadCollection }

运行我们的应用程序 ( Run Our Application )

You may run the application with yarn start. I try to call the locahost:3000/profile API with (Postman)[https://www.getpostman.com/apps], an GUI application for API testing.

您可以使用yarn start运行该应用程序。 我尝试使用(Postman)[ https://www.getpostman.com/apps]调用locahost:3000/profile API,这是用于API测试的GUI应用程序。

When I upload a file, you can see that a new file created in uploads folder and the database file db.json is created as well.

当我上传文件时,可以看到在uploads文件夹中创建了一个新文件,并且还创建了数据库文件db.json

When I issue a call without passing in avatar, error will be returned.

当我在不传递avatar情况下发出呼叫时,将返回错误。

Upload single file

过滤文件类型 ( Filter File Type )

We can handle file upload successfully now. Next, we need to limit the file type to image only. To do this, let's create a filter function that will test the file extensions.

现在,我们可以成功处理文件上传了。 接下来,我们需要将文件类型限制为仅图像。 为此,我们创建一个过滤器功能来测试文件扩展名。

// utils.ts
...

const imageFilter = function (req, file, cb) {
    // accept image only
    if (!file.originalname.match(/\.(jpg|jpeg|png|gif)$/)) {
        return cb(new Error('Only image files are allowed!'), false);
    }
    cb(null, true);
};

...

export { imageFilter, loadCollection }
应用图像过滤器 (Apply the Image Filter)

We need to tell the Multer to apply our image filter function. Add it in upload variable.

我们需要告诉Multer应用我们的图像过滤器功能。 将其添加到upload变量中。

// index.ts
...
import { imageFilter, loadCollection } from './utils';
...
// setup
...
const upload = multer({ dest: `${UPLOAD_PATH}/`, fileFilter: imageFilter }); // apply filter

...

Restart the application, try to upload a non-image file and you should get error.

重新启动应用程序,尝试上传非图像文件,您应该会收到错误消息。

上传多个文件 ( Upload Multiple Files )

Let's proceed to handle multiple files upload now. We will create a new route to allow user upload their photos.

让我们继续处理多个文件上传。 我们将创建一条新路线,以允许用户上传他们的照片。

路线 (Route)
...

app.post('/photos/upload', upload.array('photos', 12), async (req, res) => {
    try {
        const col = await loadCollection(COLLECTION_NAME, db)
        let data = [].concat(col.insert(req.files));

        db.saveDatabase();
        res.send(data.map(x => ({ id: x.$loki, fileName: x.filename, originalName: x.originalname })));
    } catch (err) {
        res.sendStatus(400);
    }
})

...

The code is similar to single file upload, except we accept a field photos instead of avatar, limit the total file to 12, accept an array of files as input and reply result as array.

该代码类似于单个文件上传,不同之处在于我们接受实地photos而不是avatar ,将总文件数限制为12,接受一个文件数组作为输入并以数组形式答复结果。

检索图像列表 ( Retrieve List of Images )

Next, create a route to retrieve all images.

接下来,创建一条路径以检索所有图像。

// index.ts
...

app.get('/images', async (req, res) => {
    try {
        const col = await loadCollection(COLLECTION_NAME, db);
        res.send(col.data);
    } catch (err) {
        res.sendStatus(400);
    }
})

...

The code is super easy to understand.

该代码超级容易理解。

通过ID检索图片 ( Retrieve Image by Id )

Next, create a route to retrieve an image by id.

接下来,创建一条通过ID检索图像的路线。

// index.ts
...

app.get('/images/:id', async (req, res) => {
    try {
        const col = await loadCollection(COLLECTION_NAME, db);
        const result = col.get(req.params.id);

        if (!result) {
            res.sendStatus(404);
            return;
        };

        res.setHeader('Content-Type', result.mimetype);
        fs.createReadStream(path.join(UPLOAD_PATH, result.filename)).pipe(res);
    } catch (err) {
        res.sendStatus(400);
    }
})

...

Notes:-

笔记:-

  1. We will return 404 if image not exist in database.

    如果图像在数据库中不存在,我们将返回404。
  2. We will stream the file as output, set the content-type correctly so our client or browser know how to handle it.

    我们将流式传输文件作为输出,正确设置content-type ,以便我们的客户端或浏览器知道如何处理它。

运行应用程序 ( Run the Application )

Now restart the application, upload a couple of images, and retrieve it by id. You should see the image is return as image instead of json object.

现在,重新启动应用程序,上传几个图像,然后按ID检索它。 您应该看到图像返回为图像而不是json对象。

Get image by id

重新启动时清除所有数据 ( Clear All Data When Restart )

Sometimes, you might want to clear all the images and database collection during development. Here's a helper function to do so.

有时,您可能需要在开发过程中清除所有图像和数据库集合。 这是一个帮助函数。

// utils.ts

....

const cleanFolder = function (folderPath) {
    // delete files inside folder but not the folder itself
    del.sync([`${folderPath}/**`, `!${folderPath}`]);
};

...

export { imageFilter, loadCollection, cleanFolder }

Use it in our application.

在我们的应用程序中使用它。

// index.ts

...
import { imageFilter, loadCollection, cleanFolder } from './utils';
...

// setup
...

// optional: clean all data before start
cleanFolder(UPLOAD_PATH);

...

摘要 ( Summary )

Handle file(s) upload with Express is easy with the help of Multer middleware. Multer is very flexible and configurable. You can go though the documentation and add more configuration as you need.

在Multer中间件的帮助下,使用Express轻松处理文件上传。 Multer非常灵活且可配置。 您可以浏览文档并根据需要添加更多配置。

The complete sourcecode is available here: https://github.com/chybie/file-upload-express.

完整的源代码可以在这里找到: https : //github.com/chybie/file-upload-express

That's it. Happy coding.

而已。 快乐的编码。

翻译自: https://scotch.io/tutorials/express-file-uploads-with-multer

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值