# 文件上传

URL: https://caijiao.org/koa/practice/upload
Source: docs/koa/practice/upload.md
Description: 文件上传是 Web 应用中常见的功能，能够让用户将本地文件上传到服务器。本节将介绍如何在 Koa 中实现文件上传功能。

在 Web 应用中，文件上传是一项非常常见的功能，允许用户将本地的文件（如图片、文档、音频等）上传到服务器。Koa 本身不提供文件上传的功能，但我们可以使用第三方中间件来处理文件上传。常用的中间件有 `koa-body` 和 `koa-multer`。

## 使用 koa-body 实现文件上传

`koa-body` 是一个 [Koa](/koa/) 中间件，能够处理表单数据（包括文件上传）。它支持多种文件上传格式，如 `multipart/form-data`，并且能够自动解析上传的文件。

### 安装 koa-body

首先，需要安装 `koa-body`：

```bash
npm install koa-body
````

### 示例：处理文件上传

```js
// app.js
const Koa = require('koa');
const Router = require('koa-router');
const koaBody = require('koa-body');
const path = require('path');
const fs = require('fs');

const app = new Koa();
const router = new Router();

// 配置 koa-body 中间件
app.use(koaBody({
  multipart: true, // 启用 multipart 解析
  formidable: {
    uploadDir: path.join(__dirname, 'uploads'), // 设置上传文件的保存目录
    keepExtensions: true, // 保留文件的扩展名
  }
}));

// 处理文件上传的路由
router.post('/upload', async (ctx) => {
  const files = ctx.request.files; // 获取上传的文件
  if (!files) {
    ctx.status = 400;
    ctx.body = { message: 'No files uploaded' };
    return;
  }

  // 返回上传文件的路径
  const uploadedFile = files.file; // 假设上传字段名称为 file
  const filePath = uploadedFile.path;

  ctx.body = {
    message: 'File uploaded successfully',
    filePath: filePath,
  };
});

// 启动服务器
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
  console.log('Server running on port 3000');
});
```

### 解释

1. `koaBody` 中间件配置：设置 `uploadDir` 来指定上传文件的保存路径，`keepExtensions` 保留文件的扩展名。
2. 在路由中，我们通过 `ctx.request.files` 获取上传的文件。
3. 文件上传成功后，返回文件的保存路径给客户端。

### 上传文件表单示例

客户端可以使用以下 HTML 表单来上传文件：

```html
<form action="/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
  <button type="submit">Upload</button>
</form>
```

## 使用 koa-multer 实现文件上传

除了 `koa-body`，`koa-multer` 也是一个常用的 Koa 文件上传中间件，功能类似，主要用于处理文件上传。

### 安装 koa-multer

首先，需要安装 `koa-multer`：

```bash
npm install koa-multer
```

### 示例：处理文件上传

```js
// app.js
const Koa = require('koa');
const Router = require('koa-router');
const multer = require('koa-multer');
const path = require('path');

const app = new Koa();
const router = new Router();

// 配置 multer 存储选项
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'uploads'); // 设置文件存储目录
  },
  filename: (req, file, cb) => {
    cb(null, `${Date.now()}-${file.originalname}`); // 设置文件名
  }
});

const upload = multer({ storage: storage });

// 处理文件上传的路由
router.post('/upload', upload.single('file'), async (ctx) => {
  if (!ctx.file) {
    ctx.status = 400;
    ctx.body = { message: 'No file uploaded' };
    return;
  }

  // 返回上传的文件路径
  const filePath = ctx.file.path;

  ctx.body = {
    message: 'File uploaded successfully',
    filePath: filePath,
  };
});

// 启动服务器
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000, () => {
  console.log('Server running on port 3000');
});
```

### 解释

1. `multer` 中间件的 `storage` 配置指定了文件存储目录和文件名。
2. 在路由中，通过 `upload.single('file')` 处理单个文件上传。
3. 文件上传成功后，返回文件的存储路径。

### 上传文件表单示例

与 `koa-body` 的上传表单相同，客户端可以使用以下 HTML 表单来上传文件：

```html
<form action="/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
  <button type="submit">Upload</button>
</form>
```

## 文件上传的安全性

1. **文件类型检查**：在处理文件上传时，建议检查文件类型，确保只允许特定类型的文件上传（如图片、PDF 等）。
2. **文件大小限制**：限制上传文件的大小，以避免服务器过载。
3. **文件名处理**：确保上传的文件名不会导致文件覆盖或路径注入等安全问题。

## 总结

文件上传是 Web 应用中常见的功能，Koa 提供了多种方式来实现文件上传，包括使用 `koa-body` 和 `koa-multer` 中间件。通过合适的中间件配置，我们可以轻松处理文件上传，并根据需求进行扩展和优化。同时，确保上传功能的安全性对于保护应用程序和用户数据至关重要。
