koa实现简单图片上传

搭建koa基础结构

  1. 新建一个文件夹
  2. 执行npm init -y
  3. 安装koa koa-router
npm install koa koa-router --save
  1. 新建app.js引入:
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
const router = new Router()

// 路由写在最下面
app.use(router.routes())
app.use(router.allowedMethods())  // 允许http请求的所有方法

app.listen(3000, (ctx) => {
  console.log('服务开启在三千端口')
})
  1. 开启服务:
    执行node app.js,命令行打印'服务开启在三千端口'表示开启成功,因为是最简单的搭建,改了任何东西都要重新执行 node app.js

编写和引入路由

  1. 根目录新建route文件夹,新建index.js
  2. 编写index.js:
const Router = require('koa-router')
const router = new Router()

router.get('/', async (ctx, next) => {
  ctx.body = ctx.request
})

module.exports = router
  1. app.js中引入,写在app.use(router.routes())的上面:
....
const index = require('./route/index')
/**
这里可以写成app.use('/', index.routes())
第一个参数代表这个文件路由的前缀,访问时就得加上设置的前缀
*/
router.use(index.routes())

app.use(router.routes())
app.use(router.allowedMethods())  // 允许http请求的所有方法
...
  1. 此时访问localhost:3000:


    image.png

处理post请求参数

文件上传请求肯定是post请求,koa中处理post请求参数需要安装一个中间件

  1. 安装koa-body
npm install koa-body --save
  1. app.js中引入和使用:
...
const koaBody = require('koa-body')

// 接收post参数解析,写在路由的前面
app.use(koaBody({
  multipart: true
}))

// userouter
...

我们处理文件上传需要在koaBody的配置设置multipart 为 true,这样上传的文件也就是formdata,会被koaBody处理在ctx.request.files中,其他普通的参数通过ctx.request.body就可以拿到:

拿文件:
const file = ctx.request.files['上传文件的字段名']

普通参数:
const data = ctx.request.body

编写上传接口与前端代码

  1. 在route文件夹下新建一个upload.js,假设规定上传的文件字段名叫file,假设只上传一个文件:
const Router = require('koa-router')
const router = new Router()

router.post('/upload', async (ctx, next) => {
  const { name, path: filePath, size, type } = ctx.request.files.file
  ctx.body = {
    name, // 文件名称
    filePath, // 临时路径
    size, // 文件大小
    type // 文件类型
  }
})

module.exports = router

上传的文件会包含上面那个几个字段,其中path为临时路径,把他们返回,下面会把接口请求结果贴出来,看一下就知道各个字段的含义。

  1. app.js中引入路由与处理跨域
    因为在本地模拟,前端我会直接写一个html文件,所以会出现跨域,这里顺便解决一下
npm install koa-cors --save

app中引入并使用:

// 放在最前面
const cors = require('koa-cors')
app.use(cors({
  origin: '*'
}))

引入upload路由:

const upload = require('./route/upload')
router.use(upload.routes())
  1. 前端代码:
<body>
  <input type="file" name="file" id="file" value="" />
  <button id="submit">上传</button>

  <script>
    document.getElementById('submit').addEventListener('click', function() {
      var file = document.getElementById('file').files[0]
      if(!file) {
        alert('请上传文件')
        return
      }
      console.log(file)

      var formdata = new FormData()
      formdata.append("file", file)

      var xhr = new XMLHttpRequest()
      xhr.open("post", "http://localhost:3000/upload")
      xhr.onreadystatechange = function() {
        if(xhr.readyState === 4 && xhr.status === 200) {
          const result = JSON.parse(xhr.response)
          console.log(result)
        }
      }
      xhr.send(formdata)
    })
  </script>
</body>

这里用了原生的ajax,返回的response是个json字符串

  1. 发送请求以及返回数据
    当我选择一个文件并点击上传后:
    image.png

    可以看到,在koa接口返回的数据这里都能正常展示,上面说到在router中,通过ctx.request.files.file拿到的path是临时路径,现在应该明确了吧,可以看到这个路径是存在我们电脑保存临时文件的地方,我们直接访问这个路径就是我们刚上传的图片,也就是说前端上传文件,在我们处理之前,koa会把我们的文件放在一个临时目录上,文件信息会放在ctx.request.files上,这时候我们只要把文件转移到我们想要的地方就可以。
  2. 移动文件
    上面说到要把文件从临时目录移动到我们想存储的地方,移动文件可以用node原生的fs模块,这里我用一个fs的拓展库:fs-extra,操作起来比较方便,api也很简单。fs-extra文档

安装: npm install fs-extra --save

改一下我们的upload接口,upload.js:

const Router = require('koa-router')
const router = new Router()
const path = require('path')
const fse = require('fs-extra')

router.post('/upload', async (ctx, next) => {
  const { name, path: filePath, size, type } = ctx.request.files.file
  const dest = path.join(__dirname, '../upload', name) // 目标目录,没有没有这个文件夹会自动创建
  await fse.move(filePath, dest) // 移动文件
  ctx.body = {
    name, // 文件名称
    filePath, // 临时路径
    size, // 文件大小
    type // 文件类型
  }
})

module.exports = router

然后我们重新上传一下文件

image.png

上传成功后,在我们koa项目的文件夹中,会自动多出来一个upload文件夹,里面就有我们上传的图片:
image.png

然后我们复制返回的filepath,也就是文件上传后的临时路径:
image.png

可以看到存在我们电脑临时目录下的文件就被移动走,相当于删除。

koa处理静态资源

其实上面已经实现了图片上传并且保存到我们想要的位置,那怎么访问呢,前端怎么展示呢,这就需要处理静态资源了。

  1. koa-static安装与使用
npm install koa-static --save

在app.js中:

const koaStatic = require('koa-static')

// 填上我们存放图片文件路径
app.use(koaStatic(path.join(__dirname, './upload/')))

这时候我们就可以直接通过图片的文件名访问图片了,我们上面上传的一张图片叫girl.jpg,这时候直接访问:

image.png

  1. 返回url与前端展示
    可以看到图片可以直接展示了,所以我们这时候只需要把图片的名称返回给前端就可以,把接口修改一下,此时访问图片的url就是图片的名称,当然看你们具体的逻辑,如果上面保存图片保存在../upload/img/xxx.jpg,那么访问图片的url就变成/img/xxx.jpg
router.post('/upload', async (ctx, next) => {
  const { name, path: filePath, size, type } = ctx.request.files.file
  const dest = path.join(__dirname, '../upload', name) // 目标目录,没有没有这个文件夹会自动创建
  await fse.move(filePath, dest) // 移动文件
  ctx.body = {
    name, // 文件名称
    filePath, // 临时路径
    size, // 文件大小
    type, // 文件类型
    url: name
  }
})

前端加个img标签展示图片:

<body>
  <input type="file" name="file" id="file" value="" />
  <button id="submit">上传</button>
  <img style="width:500px" src="" alt="">

  <script>
    document.getElementById('submit').addEventListener('click', function() {
      var file = document.getElementById('file').files[0]
      if(!file) {
        alert('请上传文件')
        return
      }
      console.log(file)

      var formdata = new FormData()
      formdata.append("file", file)

      var xhr = new XMLHttpRequest()
      xhr.open("post", "http://localhost:3000/upload")
      xhr.onreadystatechange = function() {
        if(xhr.readyState === 4 && xhr.status === 200) {
          const result = JSON.parse(xhr.response)
          console.log(result)

          document.querySelector('img').setAttribute('src', 'http://localhost:3000/' + result.url)
        }
      }
      xhr.send(formdata)
    })
  </script>
</body>

前端上传后:


image.png

添加参数

如果需要添加其他参数,就在formdata中再append其他参数

  1. 前端添加参数
...
var formdata = new FormData()
formdata.append("file", file)
// 传其他参数
formdata.append("other", "test")
...
  1. 后端处理其他参数:
router.post('/upload', async (ctx, next) => {
  const { name, path: filePath, size, type } = ctx.request.files.file
  const dest = path.join(__dirname, '../upload', name)
  await fse.move(filePath, dest, { overwrite: true })
  ctx.body = {
    name, // 文件名称
    filePath, // 临时路径
    size, // 文件大小
    type, // 文件类型
    url: name,
    data: ctx.request.body // 其他参数,会自动过滤file
  }
})

这时候把ctx.request.body返回给前端,看看是什么样的:

image.png

可以看到 ctx.request.body 会自动过滤掉上传的文件参数,通过这个我们就可以拿到其他普通的参数,这个逻辑和一些框架中的上传组件是一样的。

到这里整个功能就实现了,欢迎大家指教哦。

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

推荐阅读更多精彩内容