222
schangxiang@126.com
2025-06-13 6a8393408d8cefcea02b7a598967de8dc1e565c2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
var Busboy = require('busboy')
var chan = require('chan')
var BlackHoleStream = require('black-hole-stream')
 
var getDescriptor = Object.getOwnPropertyDescriptor
var isArray = Array.isArray
 
module.exports = function (request, options) {
  var ch = chan()
  var parts = function (fn) {
    if (fn) return ch(fn)
    return new Promise(function (resolve, reject) {
      ch(function (err, res) {
        if (err) return reject(err)
        resolve(res)
      })
    })
  }
 
  // koa special sauce
  request = request.req || request
 
  options = options || {}
  options.headers = request.headers
  // options.checkField hook `function(name, val, fieldnameTruncated, valTruncated)`
  // options.checkFile hook `function(fieldname, fileStream, filename, encoding, mimetype)`
  var checkField = options.checkField
  var checkFile = options.checkFile
  var lastError
 
  var busboy = new Busboy(options)
 
  request.on('close', cleanup)
 
  busboy
  .on('field', onField)
  .on('file', onFile)
  .on('close', cleanup)
  .on('error', onEnd)
  .on('finish', onEnd)
 
  busboy.on('partsLimit', function(){
    var err = new Error('Reach parts limit')
    err.code = 'Request_parts_limit'
    err.status = 413
    onError(err)
  })
 
  busboy.on('filesLimit', function(){
    var err = new Error('Reach files limit')
    err.code = 'Request_files_limit'
    err.status = 413
    onError(err)
  })
 
  busboy.on('fieldsLimit', function(){
    var err = new Error('Reach fields limit')
    err.code = 'Request_fields_limit'
    err.status = 413
    onError(err)
  })
 
  request.pipe(busboy)
 
  // i would just put everything in an array
  // but people will complain
  if (options.autoFields) {
    var field = parts.field = {} // object lookup
    var fields = parts.fields = [] // list lookup
  }
 
  return parts
 
  function onField(name, val, fieldnameTruncated, valTruncated) {
    if (checkField) {
      var err = checkField(name, val, fieldnameTruncated, valTruncated)
      if (err) {
        return onError(err)
      }
    }
 
    var args = [name, val, fieldnameTruncated, valTruncated]
 
    if (options.autoFields) {
      fields.push(args)
 
      // don't overwrite prototypes
      if (getDescriptor(Object.prototype, name)) return
 
      var prev = field[name]
      if (prev == null) return field[name] = val
      if (isArray(prev)) return prev.push(val)
      field[name] = [prev, val]
    } else {
      ch(args)
    }
  }
 
  function onFile(fieldname, file, filename, encoding, mimetype) {
    if (checkFile) {
      var err = checkFile(fieldname, file, filename, encoding, mimetype)
      if (err) {
        // make sure request stream's data has been read
        var blackHoleStream = new BlackHoleStream()
        file.pipe(blackHoleStream)
        return onError(err)
      }
    }
 
    // opinionated, but 5 arguments is ridiculous
    file.fieldname = fieldname
    file.filename = filename
    file.transferEncoding = file.encoding = encoding
    file.mimeType = file.mime = mimetype
    ch(file)
  }
 
  function onError(err) {
    lastError = err
  }
 
  function onEnd() {
    cleanup()
    ch(lastError)
  }
 
  function cleanup() {
    request.removeListener('close', cleanup)
    busboy.removeListener('field', onField)
    busboy.removeListener('file', onFile)
    busboy.removeListener('close', cleanup)
    busboy.removeListener('error', onEnd)
    busboy.removeListener('partsLimit', onEnd)
    busboy.removeListener('filesLimit', onEnd)
    busboy.removeListener('fieldsLimit', onEnd)
    busboy.removeListener('finish', onEnd)
  }
}