koa實(shí)現(xiàn)簡(jiǎn)單圖片上傳

搭建koa基礎(chǔ)結(jié)構(gòu)

  1. 新建一個(gè)文件夾
  2. 執(zhí)行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請(qǐng)求的所有方法

app.listen(3000, (ctx) => {
  console.log('服務(wù)開啟在三千端口')
})
  1. 開啟服務(wù):
    執(zhí)行node app.js竞思,命令行打印'服務(wù)開啟在三千端口'表示開啟成功奕剃,因?yàn)槭亲詈?jiǎn)單的搭建唠叛,改了任何東西都要重新執(zhí)行 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())
第一個(gè)參數(shù)代表這個(gè)文件路由的前綴,訪問時(shí)就得加上設(shè)置的前綴
*/
router.use(index.routes())

app.use(router.routes())
app.use(router.allowedMethods())  // 允許http請(qǐng)求的所有方法
...
  1. 此時(shí)訪問localhost:3000:


    image.png

處理post請(qǐng)求參數(shù)

文件上傳請(qǐng)求肯定是post請(qǐng)求,koa中處理post請(qǐng)求參數(shù)需要安裝一個(gè)中間件

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

// 接收post參數(shù)解析概疆,寫在路由的前面
app.use(koaBody({
  multipart: true
}))

// userouter
...

我們處理文件上傳需要在koaBody的配置設(shè)置multipart 為 true,這樣上傳的文件也就是formdata峰搪,會(huì)被koaBody處理在ctx.request.files中岔冀,其他普通的參數(shù)通過ctx.request.body就可以拿到:

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

普通參數(shù):
const data = ctx.request.body

編寫上傳接口與前端代碼

  1. 在route文件夾下新建一個(gè)upload.js,假設(shè)規(guī)定上傳的文件字段名叫file概耻,假設(shè)只上傳一個(gè)文件:
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, // 臨時(shí)路徑
    size, // 文件大小
    type // 文件類型
  }
})

module.exports = router

上傳的文件會(huì)包含上面那個(gè)幾個(gè)字段使套,其中path為臨時(shí)路徑罐呼,把他們返回,下面會(huì)把接口請(qǐng)求結(jié)果貼出來童漩,看一下就知道各個(gè)字段的含義弄贿。

  1. app.js中引入路由與處理跨域
    因?yàn)樵诒镜啬M,前端我會(huì)直接寫一個(gè)html文件矫膨,所以會(huì)出現(xiàn)跨域差凹,這里順便解決一下
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('請(qǐng)上傳文件')
        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是個(gè)json字符串

  1. 發(fā)送請(qǐng)求以及返回?cái)?shù)據(jù)
    當(dāng)我選擇一個(gè)文件并點(diǎn)擊上傳后:
    image.png

    可以看到侧馅,在koa接口返回的數(shù)據(jù)這里都能正常展示危尿,上面說到在router中,通過ctx.request.files.file拿到的path是臨時(shí)路徑馁痴,現(xiàn)在應(yīng)該明確了吧谊娇,可以看到這個(gè)路徑是存在我們電腦保存臨時(shí)文件的地方,我們直接訪問這個(gè)路徑就是我們剛上傳的圖片罗晕,也就是說前端上傳文件济欢,在我們處理之前,koa會(huì)把我們的文件放在一個(gè)臨時(shí)目錄上小渊,文件信息會(huì)放在ctx.request.files上法褥,這時(shí)候我們只要把文件轉(zhuǎn)移到我們想要的地方就可以。
  2. 移動(dòng)文件
    上面說到要把文件從臨時(shí)目錄移動(dòng)到我們想存儲(chǔ)的地方酬屉,移動(dòng)文件可以用node原生的fs模塊半等,這里我用一個(gè)fs的拓展庫:fs-extra,操作起來比較方便呐萨,api也很簡(jiǎn)單杀饵。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) // 目標(biāo)目錄谬擦,沒有沒有這個(gè)文件夾會(huì)自動(dòng)創(chuàng)建
  await fse.move(filePath, dest) // 移動(dòng)文件
  ctx.body = {
    name, // 文件名稱
    filePath, // 臨時(shí)路徑
    size, // 文件大小
    type // 文件類型
  }
})

module.exports = router

然后我們重新上傳一下文件

image.png

上傳成功后切距,在我們koa項(xiàng)目的文件夾中,會(huì)自動(dòng)多出來一個(gè)upload文件夾惨远,里面就有我們上傳的圖片:
image.png

然后我們復(fù)制返回的filepath蔚舀,也就是文件上傳后的臨時(shí)路徑:
image.png

可以看到存在我們電腦臨時(shí)目錄下的文件就被移動(dòng)走,相當(dāng)于刪除锨络。

koa處理靜態(tài)資源

其實(shí)上面已經(jīng)實(shí)現(xiàn)了圖片上傳并且保存到我們想要的位置赌躺,那怎么訪問呢,前端怎么展示呢羡儿,這就需要處理靜態(tài)資源了礼患。

  1. koa-static安裝與使用
npm install koa-static --save

在app.js中:

const koaStatic = require('koa-static')

// 填上我們存放圖片文件路徑
app.use(koaStatic(path.join(__dirname, './upload/')))

這時(shí)候我們就可以直接通過圖片的文件名訪問圖片了,我們上面上傳的一張圖片叫girl.jpg,這時(shí)候直接訪問:

image.png

  1. 返回url與前端展示
    可以看到圖片可以直接展示了缅叠,所以我們這時(shí)候只需要把圖片的名稱返回給前端就可以悄泥,把接口修改一下,此時(shí)訪問圖片的url就是圖片的名稱肤粱,當(dāng)然看你們具體的邏輯弹囚,如果上面保存圖片保存在../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) // 目標(biāo)目錄领曼,沒有沒有這個(gè)文件夾會(huì)自動(dòng)創(chuàng)建
  await fse.move(filePath, dest) // 移動(dòng)文件
  ctx.body = {
    name, // 文件名稱
    filePath, // 臨時(shí)路徑
    size, // 文件大小
    type, // 文件類型
    url: name
  }
})

前端加個(gè)img標(biāo)簽展示圖片:

<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('請(qǐng)上傳文件')
        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

添加參數(shù)

如果需要添加其他參數(shù)鸥鹉,就在formdata中再append其他參數(shù)

  1. 前端添加參數(shù)
...
var formdata = new FormData()
formdata.append("file", file)
// 傳其他參數(shù)
formdata.append("other", "test")
...
  1. 后端處理其他參數(shù):
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, // 臨時(shí)路徑
    size, // 文件大小
    type, // 文件類型
    url: name,
    data: ctx.request.body // 其他參數(shù),會(huì)自動(dòng)過濾file
  }
})

這時(shí)候把ctx.request.body返回給前端庶骄,看看是什么樣的:

image.png

可以看到 ctx.request.body 會(huì)自動(dòng)過濾掉上傳的文件參數(shù)毁渗,通過這個(gè)我們就可以拿到其他普通的參數(shù),這個(gè)邏輯和一些框架中的上傳組件是一樣的单刁。

到這里整個(gè)功能就實(shí)現(xiàn)了灸异,歡迎大家指教哦。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末羔飞,一起剝皮案震驚了整個(gè)濱河市肺樟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逻淌,老刑警劉巖么伯,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異恍风,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)誓篱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門朋贬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人窜骄,你說我怎么就攤上這事锦募。” “怎么了邻遏?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵糠亩,是天一觀的道長。 經(jīng)常有香客問我准验,道長赎线,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任糊饱,我火速辦了婚禮垂寥,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己滞项,他們只是感情好狭归,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著文判,像睡著了一般过椎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戏仓,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天疚宇,我揣著相機(jī)與錄音,去河邊找鬼柜去。 笑死灰嫉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嗓奢。 我是一名探鬼主播讼撒,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼股耽!你這毒婦竟也來了根盒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤物蝙,失蹤者是張志新(化名)和其女友劉穎炎滞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诬乞,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡册赛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了震嫉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片森瘪。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖票堵,靈堂內(nèi)的尸體忽然破棺而出扼睬,到底是詐尸還是另有隱情,我是刑警寧澤悴势,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布窗宇,位于F島的核電站,受9級(jí)特大地震影響特纤,放射性物質(zhì)發(fā)生泄漏军俊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一捧存、第九天 我趴在偏房一處隱蔽的房頂上張望蝇完。 院中可真熱鬧官硝,春花似錦、人聲如沸短蜕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朋魔。三九已至岖研,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間警检,已是汗流浹背孙援。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扇雕,地道東北人拓售。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像镶奉,于是被迫代替她去往敵國和親础淤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容