1 | #await-busboy
|
2 |
|
3 | [busboy][] multipart parser with `async/await` and [koa][]/[co][] `yield` support.
|
4 |
|
5 | [![NPM version][npm-image]][npm-url]
|
6 | [![build status][travis-image]][travis-url]
|
7 | [![Test coverage][codecov-image]][codecov-url]
|
8 | [![David deps][david-image]][david-url]
|
9 | [![npm download][download-image]][download-url]
|
10 |
|
11 | [npm-image]: https://img.shields.io/npm/v/await-busboy.svg?style=flat-square
|
12 | [npm-url]: https://npmjs.org/package/await-busboy
|
13 | [travis-image]: https://img.shields.io/travis/aheckmann/await-busboy.svg?style=flat-square
|
14 | [travis-url]: https://travis-ci.org/aheckmann/await-busboy
|
15 | [codecov-image]: https://codecov.io/github/aheckmann/await-busboy/coverage.svg?branch=master
|
16 | [codecov-url]: https://codecov.io/github/aheckmann/await-busboy?branch=master
|
17 | [david-image]: https://img.shields.io/david/aheckmann/await-busboy.svg?style=flat-square
|
18 | [david-url]: https://david-dm.org/aheckmann/await-busboy
|
19 | [download-image]: https://img.shields.io/npm/dm/await-busboy.svg?style=flat-square
|
20 | [download-url]: https://npmjs.org/package/await-busboy
|
21 | [busboy]: https://github.com/mscdex/busboy
|
22 | [co]: https://github.com/tj/co
|
23 | [koa]: https://github.com/koajs/koa
|
24 |
|
25 | _forked from https://github.com/cojs/busboy and updated to support async/await_
|
26 |
|
27 | ## Example
|
28 |
|
29 | ```js
|
30 | const Koa = require('koa')
|
31 | const app = new Koa()
|
32 | const parse = require('await-busboy')
|
33 |
|
34 | app.use(async (ctx, next) => {
|
35 | // the body isn't multipart, we can't parse it
|
36 | if (!ctx.request.is('multipart/*')) return await next
|
37 |
|
38 | const parts = parse(ctx)
|
39 |
|
40 | try {
|
41 | let part
|
42 | while ((part = await parts)) {
|
43 | if (part.length) {
|
44 | // arrays are await-busboy fields
|
45 | console.log({ key: part[0], value: part[1] })
|
46 | } else {
|
47 | // otherwise, it's a stream
|
48 | part.pipe(someOtherStream)
|
49 | }
|
50 | }
|
51 | } catch (err) {
|
52 | return ctx.throw(err)
|
53 | }
|
54 |
|
55 | ctx.body = 'await-busboy is done parsing the form!'
|
56 | });
|
57 |
|
58 | app.listen(3000);
|
59 | ```
|
60 |
|
61 | Note that parts will be delievered in the order they are defined in the form.
|
62 | Put your CSRF token first in the form and your larger files last.
|
63 |
|
64 | If you want `await-busboy` to automatically handle the fields,
|
65 | set the `autoFields: true` option.
|
66 | Now all the parts will be streams and a field object and array will automatically be populated.
|
67 |
|
68 | ```js
|
69 | const Koa = require('koa')
|
70 | const app = new Koa()
|
71 | const parse = require('await-busboy')
|
72 |
|
73 | app.use(async (ctx, next) => {
|
74 | const parts = parse(ctx, {
|
75 | autoFields: true
|
76 | })
|
77 |
|
78 | try {
|
79 | let part
|
80 | while ((part = await parts)) {
|
81 | // it's a stream
|
82 | part.pipe(fs.createWriteStream('some file.txt'))
|
83 | }
|
84 | } catch (err) {
|
85 | return ctx.throw(err)
|
86 | }
|
87 |
|
88 | ctx.body = 'and we are done parsing the form!'
|
89 |
|
90 | // .field holds all the fields in key/value form
|
91 | console.log(parts.field._csrf)
|
92 |
|
93 | // .fields holds all the fields in [key, value] form
|
94 | console.log(parts.fields[0])
|
95 | })
|
96 | ```
|
97 |
|
98 | ### Example for csrf check
|
99 |
|
100 | Use `options.checkField` hook `function(name, val, fieldnameTruncated, valTruncated)`
|
101 | can handle fields check.
|
102 |
|
103 | ```js
|
104 | const parse = require('await-busboy')
|
105 |
|
106 | app.use(async (ctx, next) => {
|
107 | const parts = parse(ctx, {
|
108 | checkField: (name, value) => {
|
109 | if (name === '_csrf' && !checkCSRF(ctx, value)) {
|
110 | const err = new Error('invalid csrf token')
|
111 | err.status = 400
|
112 | return err
|
113 | }
|
114 | }
|
115 | })
|
116 |
|
117 | let part
|
118 | while ((part = await parts)) {
|
119 | // ...
|
120 | }
|
121 | })
|
122 | ```
|
123 |
|
124 | ### Example for filename extension check
|
125 |
|
126 | Use `options.checkFile` hook `function(fieldname, file, filename, encoding, mimetype)`
|
127 | can handle filename check.
|
128 |
|
129 | ```js
|
130 | const parse = require('await-busboy')
|
131 | const path = require('path')
|
132 |
|
133 | app.use(async (ctx, next) {
|
134 | const parts = parse(ctx, {
|
135 | // only allow upload `.jpg` files
|
136 | checkFile: function (fieldname, file, filename) {
|
137 | if (path.extname(filename) !== '.jpg') {
|
138 | const err = new Error('invalid jpg image')
|
139 | err.status = 400
|
140 | return err
|
141 | }
|
142 | }
|
143 | })
|
144 |
|
145 | let part
|
146 | while ((part = await parts)) {
|
147 | // ...
|
148 | }
|
149 | })
|
150 | ```
|
151 |
|
152 | ### co, koa and yield support
|
153 |
|
154 | This module is backward compatible with [koa][], [co][] and `yield` syntax.
|
155 |
|
156 | ```js
|
157 | const Koa = require('koa')
|
158 | const app = new Koa()
|
159 | const parse = require('await-busboy')
|
160 |
|
161 | app.use(function* (ctx, next) {
|
162 | // the body isn't multipart, we can't parse it
|
163 | if (!ctx.request.is('multipart/*')) return yield next
|
164 |
|
165 | const parts = parse(ctx)
|
166 |
|
167 | try {
|
168 | let part
|
169 | while ((part = yield parts)) {
|
170 | if (part.length) {
|
171 | // arrays are await-busboy fields
|
172 | console.log({ key: part[0], value: part[1] })
|
173 | } else {
|
174 | // otherwise, it's a stream
|
175 | part.pipe(someOtherStream)
|
176 | }
|
177 | }
|
178 | } catch (err) {
|
179 | return ctx.throw(err)
|
180 | }
|
181 |
|
182 | ctx.body = 'await-busboy is done parsing the form!'
|
183 | });
|
184 | ```
|
185 |
|
186 | ## API
|
187 |
|
188 | ### parts = parse(stream, [options])
|
189 |
|
190 | ```js
|
191 | const parse = require('await-busboy')
|
192 | const parts = parse(stream, {
|
193 | autoFields: true
|
194 | })
|
195 | ```
|
196 |
|
197 | `options` are passed to [busboy][].
|
198 | The only additional option is `autoFields`.
|
199 |
|
200 | **Note**: If [busboy][] events `partsLimit`, `filesLimit`, `fieldsLimit` is emitted, will throw an error.
|
201 |
|
202 | ### part = await parts
|
203 |
|
204 | await the next part.
|
205 | If `autoFields: true`, this will always be a file stream.
|
206 | Otherwise, it will be a [field](https://github.com/mscdex/busboy#busboy-special-events) as an array.
|
207 |
|
208 | - Readable Stream
|
209 |
|
210 | - `fieldname`
|
211 | - `filename`
|
212 | - `transferEncoding` or `encoding`
|
213 | - `mimeType` or `mime`
|
214 |
|
215 | - Field[]
|
216 |
|
217 | 0. `fieldname`
|
218 | 1. `value`
|
219 | 2. `valueTruncated` - `Boolean`
|
220 | 3. `fieldnameTruncated` - Boolean
|
221 |
|
222 | If falsey, then the parser is done.
|
223 |
|
224 | ### parts.field{}
|
225 |
|
226 | If `autoFields: true`, this object will be populated with key/value pairs.
|
227 |
|
228 | ### parts.fields[]
|
229 |
|
230 | If `autoFields: true`, this array will be populated with all fields.
|
231 |
|
232 | ## Development
|
233 |
|
234 | ### Running tests
|
235 |
|
236 | - `npm test` runs tests + code coverage + lint
|
237 | - `npm run lint` runs lint only
|
238 | - `npm run lint-fix` runs lint and attempts to fix syntax issues
|
239 | - `npm run test-cov` runs tests + test coverage
|
240 | - `npm run open-cov` opens test coverage results in your browser
|
241 | - `npm run test-only` runs tests only
|
242 |
|
243 | ## LICENSE
|
244 |
|
245 | [MIT](https://github.com/aheckmann/await-busboy/blob/master/LICENSE)
|