koa 实现上传文件

项目目录:javascript

1.上传单个文件html

思路:java

(1)获取上传文件,使用 const file = ctx.request.files.fileios

(2)咱们使用 fs.createReadStream 来读取文件流;如代码:const fileReader = fs.createReadStream(file.path); axios

(3)对当前上传的文件保存到 /static/upload 目录下,所以定义变量:const filePath = path.join(__dirname, '/static/upload/');数组

(4) 组装文件的绝对路径,代码:const fileResource = filePath + `/${file.name}`;app

(5)使用 fs.createWriteStream 把该文件写进去,如代码:const writeStream = fs.createWriteStream(fileResource);koa

(6) 下面这段代码就是判断是否有该目录,若是没有改目录,就建立一个 /static/upload 这个目录,若是有就直接使用管道流pipe拼接文件,如代码:fileReader.pipe(writeStream);post

if (!fs.existsSync(filePath)) {
  fs.mkdir(filePath, (err) => {
    if (err) {
      throw new Error(err);
    } else {
      fileReader.pipe(writeStream);
      ctx.body = {
        url: uploadUrl + `/${file.name}`,
        code: 0,
        message: '上传成功'
      };
    }
  });
} else {
  fileReader.pipe(writeStream);
  ctx.body = {
    url: uploadUrl + `/${file.name}`,
    code: 0,
    message: '上传成功'
  };
}

最后咱们使用 ctx.body 返回到页面来,所以若是咱们上传成功了,就会在upload页面返回以下信息了;以下图所示:ui

源码:

static/upload.html

<!DOCTYPE html>
<html>
<head>
  <meta charset=utf-8>
  <title>文件上传</title>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
  <!-- 使用form表单提交
  <form action="http://localhost:3001/upload" method="post" enctype="multipart/form-data">
    <div>
      <input type="file" name="file">
    </div>
    <div>
      <input type="submit" value="提交"/>
    </div>
  </form>
  -->
  <div>
    <input type="file" name="file" id="file">
  </div>
  <script type="text/javascript">
    var file = document.getElementById('file');
    const instance = axios.create({
      withCredentials: true
    });
    file.onchange = function(e) {
      var f1 = e.target.files[0];
      var fdata = new FormData();
      fdata.append('file', f1);
      instance.post('http://localhost:3001/upload', fdata).then(res => {
        console.log(res);
      }).catch(err => {
        console.log(err);
      });
    }
  </script>
</body>
</html>

app.js

// 引入模块
const Koa = require('koa');
const fs = require('fs');
const path = require('path');
const router = require('koa-router')();
const koaBody = require('koa-body');
const static = require('koa-static');

// 实例化
const app = new Koa();

app.use(koaBody({
  multipart: true, // 支持文件上传
  formidable: {
    maxFieldsSize: 2 * 1024 * 1024, // 最大文件为2兆
    multipart: true // 是否支持 multipart-formdate 的表单
  }
}));

const uploadUrl = "http://localhost:3001/static/upload";

// 配置路由
router.get('/', (ctx) => {
  // 设置头类型, 若是不设置,会直接下载该页面
  ctx.type = 'html';
  // 读取文件
  const pathUrl = path.join(__dirname, '/static/upload.html');
  ctx.body = fs.createReadStream(pathUrl);
});

// 上传文件
router.post('/upload', (ctx) => {
  // 获取上传文件
  const file = ctx.request.files.file;
  console.log(file);
  // 读取文件流
  const fileReader = fs.createReadStream(file.path);
  console.log(fileReader);
  // 设置文件保存路径
  const filePath = path.join(__dirname, '/static/upload/');
  // 组装成绝对路径
  const fileResource = filePath + `/${file.name}`;

  /**
   * 使用 createWriteStream 写入数据,而后使用管道流pipe拼接
   */
  const writeStream = fs.createWriteStream(fileResource);
  // 判断 /static/upload 文件夹是否存在,若是不在的话就建立一个
  if (!fs.existsSync(filePath)) {
    fs.mkdir(filePath, (err) => {
      if (err) {
        throw new Error(err);
      } else {
        fileReader.pipe(writeStream);
        ctx.body = {
          url: uploadUrl + `/${file.name}`,
          code: 0,
          message: '上传成功'
        };
      }
    });
  } else {
    fileReader.pipe(writeStream);
    ctx.body = {
      url: uploadUrl + `/${file.name}`,
      code: 0,
      message: '上传成功'
    };
  }
});

// 配置静态资源路径
app.use(static(path.join(__dirname)));

// 启动路由
app.use(router.routes()).use(router.allowedMethods());

// 监听端口号
app.listen(3001, () => {
  console.log('server is listen in 3001');
});

2.上传多个文件

为了支持多个文件上传,和单个文件上传,咱们须要把代码改下,改为以下:

static/upload.html

<!DOCTYPE html>
<html>

<head>
  <meta charset=utf-8>
  <title>文件上传</title>
  <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>

<body>
  <!-- 使用form表单提交
  <form action="http://localhost:3001/upload" method="post" enctype="multipart/form-data">
    <div>
      <input type="file" name="file">
    </div>
    <div>
      <input type="submit" value="提交"/>
    </div>
  </form>
  -->
  <!--  上传单个文件
  <div>
    <input type="file" name="file" id="file">
  </div>
  <script type="text/javascript">
    var file = document.getElementById('file');
    const instance = axios.create({
      withCredentials: true
    });
    file.onchange = function(e) {
      var f1 = e.target.files[0];
      var fdata = new FormData();
      fdata.append('file', f1);
      instance.post('http://localhost:3001/upload', fdata).then(res => {
        console.log(res);
      }).catch(err => {
        console.log(err);
      });
    }
  </script>
  -->
  <div>
    <input type="file" name="file" id="file" multiple="multiple">
  </div>
  <script type="text/javascript">
    var file = document.getElementById('file');
    const instance = axios.create({
      withCredentials: true
    });
    file.onchange = function (e) {
      var files = e.target.files;
      var fdata = new FormData();
      if (files.length > 0) {
        for (let i = 0; i < files.length; i++) {
          const f1 = files[i];
          fdata.append('file', f1);
        }
      }
      instance.post('http://localhost:3001/upload', fdata).then(res => {
        console.log(res);
      }).catch(err => {
        console.log(err);
      });
    }
  </script>
</body>

</html>

如上是多个文件上传的html代码和js代码,就是把多个数据使用formdata一次性传递多个数据过去,如今咱们须要把app.js 代码改为以下了,app.js 代码改的有点多,最主要是要判断 传过来的文件是单个的仍是多个的逻辑,全部代码以下:

// 引入模块
const Koa = require('koa');
const fs = require('fs');
const path = require('path');
const router = require('koa-router')();
const koaBody = require('koa-body');
const static = require('koa-static');

// 实例化
const app = new Koa();

app.use(koaBody({
  multipart: true, // 支持文件上传
  formidable: {
    maxFieldsSize: 2 * 1024 * 1024, // 最大文件为2兆
    multipart: true // 是否支持 multipart-formdate 的表单
  }
}));

const uploadUrl = "http://localhost:3001/static/upload";

router.get('/', (ctx) => {
  // 设置头类型, 若是不设置,会直接下载该页面
  ctx.type = 'html';
  // 读取文件
  const pathUrl = path.join(__dirname, '/static/upload.html');
  ctx.body = fs.createReadStream(pathUrl);
});

/**
 * flag: 是不是多个文件上传
 */
const uploadFilePublic = function (ctx, files, flag) {
  const filePath = path.join(__dirname, '/static/upload/');
  let file,
    fileReader,
    fileResource,
    writeStream;

  const fileFunc = function (file) {
    // 读取文件流
    fileReader = fs.createReadStream(file.path);
    // 组装成绝对路径
    fileResource = filePath + `/${file.name}`;
    /*
     使用 createWriteStream 写入数据,而后使用管道流pipe拼接
    */
    writeStream = fs.createWriteStream(fileResource);
    fileReader.pipe(writeStream);
  };
  const returnFunc = function (flag) {
    console.log(flag);
    console.log(files);
    if (flag) {
      let url = '';
      for (let i = 0; i < files.length; i++) {
        url += uploadUrl + `/${files[i].name},`
      }
      url = url.replace(/,$/gi, "");
      ctx.body = {
        url: url,
        code: 0,
        message: '上传成功'
      };
    } else {
      ctx.body = {
        url: uploadUrl + `/${files.name}`,
        code: 0,
        message: '上传成功'
      };
    }
  };
  if (flag) {
    // 多个文件上传
    for (let i = 0; i < files.length; i++) {
      const f1 = files[i];
      fileFunc(f1);
    }
  } else {
    fileFunc(files);
  }

  // 判断 /static/upload 文件夹是否存在,若是不在的话就建立一个
  if (!fs.existsSync(filePath)) {
    fs.mkdir(filePath, (err) => {
      if (err) {
        throw new Error(err);
      } else {
        returnFunc(flag);
      }
    });
  } else {
    returnFunc(flag);
  }
}

// 上传单个或多个文件
router.post('/upload', (ctx) => {
  let files = ctx.request.files.file;
  const fileArrs = [];
  if (files.length === undefined) {
    // 上传单个文件,它不是数组,只是单个的对象
    uploadFilePublic(ctx, files, false);
  } else {
    uploadFilePublic(ctx, files, true);
  }
});

// 配置静态资源路径
app.use(static(path.join(__dirname)));

// 启动路由
app.use(router.routes()).use(router.allowedMethods());

// 监听端口号
app.listen(3001, () => {
  console.log('server is listen in 3001');
});

而后我如今来演示下,当我选择多个文件,好比如今选择两个文件,会返回以下数据:

当我如今只选择一个文件的时候,只会返回一个文件,以下图所示:

如上app.js改为以后的代码如今支持单个或多个文件上传了。

转自:https://www.cnblogs.com/tugenhua0707/p/10828869.html

.