koa2-note icon indicating copy to clipboard operation
koa2-note copied to clipboard

upload方法监听不到file上传方法

Open xjl271314 opened this issue 5 years ago • 5 comments

` Request Headers: Accept: / Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Connection: keep-alive Content-Length: 1243 Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0nc6DwwRlVIQdGiJ Cookie: captcha_token=408f7903a848baed9d5878f16cb73844; USER_SID=lCyHKbOwfu6Kle9RdT37AUW3mAHzp-BR Host: localhost:8000 Origin: http://localhost:8000 Referer: http://localhost:8000/ User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36 X-Requested-With: XMLHttpRequest

Request payload

------WebKitFormBoundary0nc6DwwRlVIQdGiJ Content-Disposition: form-data; name="file"; filename="[email protected]" Content-Type: image/png

------WebKitFormBoundary0nc6DwwRlVIQdGiJ-- ` 文件上传前端上传的数据是这样的

服务端用的koa2,上传方法是你的uploadFile方法,但是在调用的时候

// 解析请求文件事件 busboy.on('file', )这个方法并不会触发,我暂时还不知道如何解决,能否给予帮助,谢谢

xjl271314 avatar Jul 09 '18 07:07 xjl271314

请问,能否把完整的代码贴出来么?这样子信息太少了

chenshenhai avatar Jul 09 '18 13:07 chenshenhai

好的,麻烦了,我等明天早上回复,我研究了一段时间还是不知道问题出在哪

xjl271314 avatar Jul 09 '18 13:07 xjl271314

` //麻烦了,这里是前端部分代码 用的是ant design的upload组件

<Upload
       name="file"
       action="http://localhost:8000/api/goods/imgUpload"
       accept="image/*"
       data={(file)=>{this.upload(file)}}
       withCredentials
       listType="picture-card"
       fileList={fileList}
       onPreview={this.handlePreview}
       onChange={this.handleChange}
 >
      {fileList.length >= 10 ? null : uploadButton}
</Upload>

//点击上传后 Request Header

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Connection: keep-alive
Content-Length: 814
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryZvGXdj5g8yvDOCFD
Cookie: USER_SID=qIk6gw9xjGBH4dnZSiID6ay4H7fMzyzA
Host: localhost:8000
Origin: http://localhost:8000
Referer: http://localhost:8000/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36
X-Requested-With: XMLHttpRequest

//发现upload组件上传的信息在 request payload里面

------WebKitFormBoundaryZvGXdj5g8yvDOCFD
Content-Disposition: form-data; name="file"; filename="jiekuan_beika.png"
Content-Type: image/png

------WebKitFormBoundaryZvGXdj5g8yvDOCFD--

//这里是node koa2的主入口app.js

require("babel-register");
require("babel-polyfill");

const path = require('path')
const Koa = require('koa')
const convert = require('koa-convert')
const views = require('koa-views')
const koaStatic = require('koa-static')
const bodyParser = require('koa-bodyparser')
const koaLogger = require('koa-logger')
const session = require('koa-session-minimal')
const MysqlStore = require('koa-mysql-session')
const json = require('koa-json')
const koaBody = require('koa-body');


const errorHandle = require('./controllers/error-catch')
const config = require('./../config')
const routers = require('./routers/index')

const app = new Koa()

// session存储配置
const sessionMysqlConfig = {
  user: config.database.USERNAME,
  password: config.database.PASSWORD,
  database: config.database.DATABASE,
  host: config.database.HOST,
}
//配置错误处理中间件
app.use(errorHandle())
// 配置session中间件
const THIRTY_MINTUES = 30 * 60 * 1000;
const staticPath = "static";
app.use(session({
  key: 'USER_SID',
  store: new MysqlStore(sessionMysqlConfig),
  cookie: {
    maxAge: THIRTY_MINTUES,
    overwrite: false,
    rolling: true, //每次api请求是否重置session有效期 默认为fale
    renew: false,
  }
}))

app.use(koaStatic(
  path.join( __dirname,  staticPath)
))

// 配置控制台日志中间件
app.use(koaLogger())

// 配置ctx.body解析中间件
app.use(koaBody());
app.use(bodyParser(
  {
    enableTypes: ['json', 'form', 'text']
  }
))

//配置json解析中间件
app.use(json())

// 初始化路由中间件
app.use(routers.routes()).use(routers.allowedMethods())

// 监听启动端口
app.listen(config.port)
console.log(`the server is start at port ${config.port}`)

//上传图片API在controllers里面

    router.post('/goods/imgUpload',goodsController.imgUpload )

    /**
     * 上传商品图片 2018-07-06
     * @param  {obejct} ctx 上下文对象
     */
    async imgUpload(ctx, next) {
        let result = new WebResult(ctx.request);

        let response = await uploadFile(ctx,{});

        if(response.success){
            result.set(1,'调用成功')
        }
        ctx.body = response
    }

//这里是upload主要方法

const inspect = require('util').inspect
const path = require('path')
const os = require('os')
const fs = require('fs')
const UtilType = require('./type')
const UtilDatetime = require('./datetime')
const Busboy = require('busboy')
import Appconfig from '../../appConfig'

/**
 * 同步创建文件目录
 * @param  {string} dirname 目录绝对地址
 * @return {boolean}        创建目录结果
 */
function mkdirsSync(dirname) {
  if (fs.existsSync(dirname)) {
    return true
  } else {
    if (mkdirsSync(path.dirname(dirname))) {
      fs.mkdirSync(dirname)
      return true
    }
  }
}

/**
 * 获取上传文件的后缀名
 * @param  {string} fileName 获取上传文件的后缀名
 * @return {string}          文件后缀名
 */
function getSuffixName(fileName) {
  let nameList = fileName.split('.')
  return nameList[nameList.length - 1]
}

/**
 * 上传文件
 * @param  {object} ctx     koa上下文
 * @param  {object} options 文件上传参数 fileType文件类型, path文件存放路径
 * @return {promise}         
 */
function uploadFile(ctx, options) {
  let req = ctx.req
  let res = ctx.res
  let busboy = new Busboy({
    headers: req.headers,
  })
  // 获取类型
  let fileType = options.fileType || 'common'
  //创建路径
  let filePath = path.join(
    __dirname,
    '/../../server/static/upload/',
    fileType,
    UtilDatetime.parseStampToFormat(null, 'YYYY/MM/DD')
  )
  let mkdirResult = mkdirsSync(filePath)
  return new Promise((resolve, reject) => {

    let result = {
      code: -1,
      success: false,
      message: '',
      data: null,
    }

    // 解析请求文件事件
    busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
      console.log('文件上传中...')
      let fileName = Math.random().toString(16).substr(2) + '.' + getSuffixName(filename)
      let _uploadFilePath = path.join(filePath, fileName)
      let saveTo = path.join(_uploadFilePath)

      // 文件保存到制定路径
      file.pipe(fs.createWriteStream(saveTo))

      // 文件写入事件结束
      file.on('end', function () {
        result.success = true
        result.message = '文件上传成功'
        result.data = {
          pictureUrl: `//${ctx.host}/image/${fileType}/${fileName}`
        }
        console.log('文件上传成功!')
        resolve(result)
      })
    })

    // 解析表单中其他字段信息
    busboy.on('field', function (fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) {
      console.log('表单字段数据 [' + fieldname + ']: value: ' + inspect(val));
      result.data[fieldname] = inspect(val);
    });

    // 解析结束事件
    busboy.on('finish', function () {
      console.log('文件上传结束')
      resolve(result)
    })

    // 解析错误事件
    busboy.on('error', function (err) {
      console.log('文件上出错')
      reject(result)
    })
    //将流链接到busboy对象
    req.pipe(busboy);
  })
}


module.exports = {
  uploadFile
}

现在的问题就是在上传的时候,前端构造的上传数据我认为是正确的,但是去koa2接收的时候,直接输出了文件上传结束,实际是文件夹创建成功,但是图片没有上传成功,好像ctx里面并没有读取到上传的信息

//这里是busboy输出结果

Busboy {
  _writableState:
   WritableState {
     objectMode: false,
     highWaterMark: 16384,
     finalCalled: false,
     needDrain: false,
     ending: false,
     ended: false,
     finished: false,
     destroyed: false,
     decodeStrings: true,
     defaultEncoding: 'utf8',
     length: 0,
     writing: false,
     corked: 0,
     sync: true,
     bufferProcessing: false,
     onwrite: [Function: bound onwrite],
     writecb: null,
     writelen: 0,
     bufferedRequest: null,
     lastBufferedRequest: null,
     pendingcb: 0,
     prefinished: false,
     errorEmitted: false,
     bufferedRequestCount: 0,
     corkedRequestsFree:
      { next: null,
        entry: null,
        finish: [Function: bound onCorkedFinish] } },
  writable: true,
  domain: null,
  _events: {},
  _eventsCount: 0,
  _maxListeners: undefined,
  _done: false,
  _parser:
   Multipart {
     _needDrain: false,
     _pause: false,
     _cb: undefined,
     _nparts: 0,
     _boy: [Circular],
     parser:
      Dicer {
        _writableState: [Object],
        writable: true,
        domain: null,
        _events: [Object],
        _eventsCount: 4,
        _maxListeners: undefined,
        _bparser: [Object],
        _headerFirst: undefined,
        _dashes: 0,
        _parts: 0,
        _finished: false,
        _realFinish: false,
        _isPreamble: true,
        _justMatched: false,
        _firstWrite: true,
        _inHeader: true,
        _part: undefined,
        _cb: undefined,
        _ignoreData: false,
        _partOpts: {},
        _pause: false,
        _hparser: [Object] } },
  _finished: false,
  opts:
   { headers:
      { origin: 'http://localhost:8000',
        'x-requested-with': 'XMLHttpRequest',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
        'content-type': 'multipart/form-data; boundary=----WebKitFormBoundaryNElS5SQ4CTXinRrG',
        accept: '*/*',
        referer: 'http://localhost:8000/',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8',
        cookie: 'USER_SID=qIk6gw9xjGBH4dnZSiID6ay4H7fMzyzA',
        connection: 'close',
        'content-length': '2',
        'accept-charset': 'utf-8',
        host: 'localhost:3001' } } }

xjl271314 avatar Jul 10 '18 01:07 xjl271314

我猜测是不是busboy与koa-bodyParse解析中间件冲突了 ,导致文件上传失败?

xjl271314 avatar Jul 10 '18 10:07 xjl271314

busboy 与koa-body 是冲突的,可以试试在app.js 中注释掉koa-body,就可以正常回调 file 方法了。如果使用了koa-body,可以直接使用它来实现上传功能,不需要再使用busboy了。

xiuxiuing avatar Mar 15 '19 15:03 xiuxiuing