You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

README.md 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. <p align="center">
  2. <img alt="npm formidable package logo" src="https://raw.githubusercontent.com/node-formidable/formidable/master/logo.png" />
  3. </p>
  4. # formidable [![npm version][npmv-img]][npmv-url] [![MIT license][license-img]][license-url] [![Libera Manifesto][libera-manifesto-img]][libera-manifesto-url] [![Twitter][twitter-img]][twitter-url]
  5. > A Node.js module for parsing form data, especially file uploads.
  6. ### Important Notes
  7. - This README is for the upcoming (end of February) v2 release!
  8. - Every version prior and including `v1.2.2` is deprecated, please upgrade!
  9. - Install with `formidable@canary` until v2 land officially in `latest`
  10. - see more about the changes in the [CHANGELOG.md](https://github.com/node-formidable/formidable/blob/master/CHANGELOG.md)
  11. [![Code style][codestyle-img]][codestyle-url]
  12. [![codecoverage][codecov-img]][codecov-url]
  13. [![linux build status][linux-build-img]][build-url]
  14. [![windows build status][windows-build-img]][build-url]
  15. [![macos build status][macos-build-img]][build-url]
  16. If you have any _how-to_ kind of questions, please read the [Contributing
  17. Guide][contributing-url] and [Code of Conduct][code_of_conduct-url]
  18. documents.<br /> For bugs reports and feature requests, [please create an
  19. issue][open-issue-url] or ping [@tunnckoCore](https://twitter.com/tunnckoCore)
  20. at Twitter.
  21. [![Conventional Commits][ccommits-img]][ccommits-url]
  22. [![Minimum Required Nodejs][nodejs-img]][npmv-url]
  23. [![Tidelift Subcsription][tidelift-img]][tidelift-url]
  24. [![Buy me a Kofi][kofi-img]][kofi-url]
  25. [![Renovate App Status][renovateapp-img]][renovateapp-url]
  26. [![Make A Pull Request][prs-welcome-img]][prs-welcome-url]
  27. This project is [semantically versioned](https://semver.org) and available as
  28. part of the [Tidelift Subscription][tidelift-url] for professional grade
  29. assurances, enhanced support and security.
  30. [Learn more.](https://tidelift.com/subscription/pkg/npm-formidable?utm_source=npm-formidable&utm_medium=referral&utm_campaign=enterprise)
  31. _The maintainers of `formidable` and thousands of other packages are working
  32. with Tidelift to deliver commercial support and maintenance for the Open Source
  33. dependencies you use to build your applications. Save time, reduce risk, and
  34. improve code health, while paying the maintainers of the exact dependencies you
  35. use._
  36. [![][npm-weekly-img]][npmv-url] [![][npm-monthly-img]][npmv-url]
  37. [![][npm-yearly-img]][npmv-url] [![][npm-alltime-img]][npmv-url]
  38. ## Status: Maintained [![npm version][npmv-canary-img]][npmv-url]
  39. This module was initially developed by
  40. [**@felixge**](https://github.com/felixge) for
  41. [Transloadit](http://transloadit.com/), a service focused on uploading and
  42. encoding images and videos. It has been battle-tested against hundreds of GBs of
  43. file uploads from a large variety of clients and is considered production-ready
  44. and is used in production for years.
  45. Currently, we are few maintainers trying to deal with it. :) More contributors
  46. are always welcome! :heart: Jump on
  47. [issue #412](https://github.com/felixge/node-formidable/issues/412) which is
  48. closed, but if you are interested we can discuss it and add you after strict rules, like
  49. enabling Two-Factor Auth in your npm and GitHub accounts.
  50. _**Note:** The github `master` branch is a "canary" branch - try it with `npm i formidable@canary`.
  51. Do not expect (for now) things from it to be inside the`latest` "dist-tag" in the
  52. Npm. The`formidable@latest`is the`v1.2.1` version and probably it will be the
  53. last`v1` release!_
  54. _**Note: v2 is coming soon!**_
  55. ## Highlights
  56. - [Fast (~900-2500 mb/sec)](#benchmarks) & streaming multipart parser
  57. - Automatically writing file uploads to disk (soon optionally)
  58. - [Plugins API](#useplugin-plugin) - allowing custom parsers and plugins
  59. - Low memory footprint
  60. - Graceful error handling
  61. - Very high test coverage
  62. ## Install
  63. This project requires `Node.js >= 10.13`. Install it using
  64. [yarn](https://yarnpkg.com) or [npm](https://npmjs.com).<br /> _We highly
  65. recommend to use Yarn when you think to contribute to this project._
  66. ```sh
  67. npm install formidable
  68. # or the canary version
  69. npm install formidable@canary
  70. ```
  71. or with Yarn v1/v2
  72. ```sh
  73. yarn add formidable
  74. # or the canary version
  75. yarn add formidable@canary
  76. ```
  77. This is a low-level package, and if you're using a high-level framework it _may_
  78. already be included. Check the examples below and the `examples/` folder.
  79. ## Examples
  80. For more examples look at the `examples/` directory.
  81. ### with Node.js http module
  82. Parse an incoming file upload, with the
  83. [Node.js's built-in `http` module](https://nodejs.org/api/http.html).
  84. ```js
  85. const http = require('http');
  86. const formidable = require('formidable');
  87. const server = http.createServer((req, res) => {
  88. if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
  89. // parse a file upload
  90. const form = formidable({ multiples: true });
  91. form.parse(req, (err, fields, files) => {
  92. res.writeHead(200, { 'content-type': 'application/json' });
  93. res.end(JSON.stringify({ fields, files }, null, 2));
  94. });
  95. return;
  96. }
  97. // show a file upload form
  98. res.writeHead(200, { 'content-type': 'text/html' });
  99. res.end(`
  100. <h2>With Node.js <code>"http"</code> module</h2>
  101. <form action="/api/upload" enctype="multipart/form-data" method="post">
  102. <div>Text field title: <input type="text" name="title" /></div>
  103. <div>File: <input type="file" name="multipleFiles" multiple="multiple" /></div>
  104. <input type="submit" value="Upload" />
  105. </form>
  106. `);
  107. });
  108. server.listen(8080, () => {
  109. console.log('Server listening on http://localhost:8080/ ...');
  110. });
  111. ```
  112. ### with Express.js
  113. There are multiple variants to do this, but Formidable just need Node.js Request
  114. stream, so something like the following example should work just fine, without
  115. any third-party [Express.js](https://ghub.now.sh/express) middleware.
  116. Or try the
  117. [examples/with-express.js](https://github.com/node-formidable/node-formidable/blob/master/examples/with-express.js)
  118. ```js
  119. const express = require('express');
  120. const formidable = require('formidable');
  121. const app = express();
  122. app.get('/', (req, res) => {
  123. res.send(`
  124. <h2>With <code>"express"</code> npm package</h2>
  125. <form action="/api/upload" enctype="multipart/form-data" method="post">
  126. <div>Text field title: <input type="text" name="title" /></div>
  127. <div>File: <input type="file" name="someExpressFiles" multiple="multiple" /></div>
  128. <input type="submit" value="Upload" />
  129. </form>
  130. `);
  131. });
  132. app.post('/api/upload', (req, res, next) => {
  133. const form = formidable({ multiples: true });
  134. form.parse(req, (err, fields, files) => {
  135. if (err) {
  136. next(err);
  137. return;
  138. }
  139. res.json({ fields, files });
  140. });
  141. });
  142. app.listen(3000, () => {
  143. console.log('Server listening on http://localhost:3000 ...');
  144. });
  145. ```
  146. ### with Koa and Formidable
  147. Of course, with [Koa v1, v2 or future v3](https://ghub.now.sh/koa) the things
  148. are very similar. You can use `formidable` manually as shown below or through
  149. the [koa-better-body](https://ghub.now.sh/koa-better-body) package which is
  150. using `formidable` under the hood and support more features and different
  151. request bodies, check its documentation for more info.
  152. _Note: this example is assuming Koa v2. Be aware that you should pass `ctx.req`
  153. which is Node.js's Request, and **NOT** the `ctx.request` which is Koa's Request
  154. object - there is a difference._
  155. ```js
  156. const Koa = require('koa');
  157. const formidable = require('formidable');
  158. const app = new Koa();
  159. app.on('error', (err) => {
  160. console.error('server error', err);
  161. });
  162. app.use(async (ctx, next) => {
  163. if (ctx.url === '/api/upload' && ctx.method.toLowerCase() === 'post') {
  164. const form = formidable({ multiples: true });
  165. // not very elegant, but that's for now if you don't want touse `koa-better-body`
  166. // or other middlewares.
  167. await new Promise((resolve, reject) => {
  168. form.parse(ctx.req, (err, fields, files) => {
  169. if (err) {
  170. reject(err);
  171. return;
  172. }
  173. ctx.set('Content-Type', 'application/json');
  174. ctx.status = 200;
  175. ctx.state = { fields, files };
  176. ctx.body = JSON.stringify(ctx.state, null, 2);
  177. resolve();
  178. });
  179. });
  180. await next();
  181. return;
  182. }
  183. // show a file upload form
  184. ctx.set('Content-Type', 'text/html');
  185. ctx.status = 200;
  186. ctx.body = `
  187. <h2>With <code>"koa"</code> npm package</h2>
  188. <form action="/api/upload" enctype="multipart/form-data" method="post">
  189. <div>Text field title: <input type="text" name="title" /></div>
  190. <div>File: <input type="file" name="koaFiles" multiple="multiple" /></div>
  191. <input type="submit" value="Upload" />
  192. </form>
  193. `;
  194. });
  195. app.use((ctx) => {
  196. console.log('The next middleware is called');
  197. console.log('Results:', ctx.state);
  198. });
  199. app.listen(3000, () => {
  200. console.log('Server listening on http://localhost:3000 ...');
  201. });
  202. ```
  203. ## Benchmarks (for v2)
  204. The benchmark is quite old, from the old codebase. But maybe quite true though.
  205. Previously the numbers was around ~500 mb/sec. Currently with moving to the new
  206. Node.js Streams API it's faster. You can clearly see the differences between the
  207. Node versions.
  208. _Note: a lot better benchmarking could and should be done in future._
  209. Benchmarked on 8GB RAM, Xeon X3440 (2.53 GHz, 4 cores, 8 threads)
  210. ```
  211. ~/github/node-formidable master
  212. ❯ nve --parallel 8 10 12 13 node benchmark/bench-multipart-parser.js
  213. ⬢ Node 8
  214. 1261.08 mb/sec
  215. ⬢ Node 10
  216. 1113.04 mb/sec
  217. ⬢ Node 12
  218. 2107.00 mb/sec
  219. ⬢ Node 13
  220. 2566.42 mb/sec
  221. ```
  222. ![benchmark January 29th, 2020](./benchmark-2020-01-29_xeon-x3440.png)
  223. ## API
  224. ### Formidable / IncomingForm
  225. All shown are equivalent.
  226. _Please pass [`options`](#options) to the function/constructor, not by assigning
  227. them to the instance `form`_
  228. ```js
  229. const formidable = require('formidable');
  230. const form = formidable(options);
  231. // or
  232. const { formidable } = require('formidable');
  233. const form = formidable(options);
  234. // or
  235. const { IncomingForm } = require('formidable');
  236. const form = new IncomingForm(options);
  237. // or
  238. const { Formidable } = require('formidable');
  239. const form = new Formidable(options);
  240. ```
  241. ### Options
  242. See it's defaults in [src/Formidable.js](./src/Formidable.js#L14-L22) (the
  243. `DEFAULT_OPTIONS` constant).
  244. - `options.encoding` **{string}** - default `'utf-8'`; sets encoding for
  245. incoming form fields,
  246. - `options.uploadDir` **{string}** - default `os.tmpdir()`; the directory for
  247. placing file uploads in. You can move them later by using `fs.rename()`
  248. - `options.keepExtensions` **{boolean}** - default `false`; to include the
  249. extensions of the original files or not
  250. - `options.maxFileSize` **{number}** - default `200 * 1024 * 1024` (200mb);
  251. limit the size of uploaded file.
  252. - `options.maxFields` **{number}** - default `1000`; limit the number of fields
  253. that the Querystring parser will decode, set 0 for unlimited
  254. - `options.maxFieldsSize` **{number}** - default `20 * 1024 * 1024` (20mb);
  255. limit the amount of memory all fields together (except files) can allocate in
  256. bytes.
  257. - `options.hash` **{boolean}** - default `false`; include checksums calculated
  258. for incoming files, set this to some hash algorithm, see
  259. [crypto.createHash](https://nodejs.org/api/crypto.html#crypto_crypto_createhash_algorithm_options)
  260. for available algorithms
  261. - `options.multiples` **{boolean}** - default `false`; when you call the
  262. `.parse` method, the `files` argument (of the callback) will contain arrays of
  263. files for inputs which submit multiple files using the HTML5 `multiple`
  264. attribute. Also, the `fields` argument will contain arrays of values for
  265. fields that have names ending with '[]'.
  266. _**Note:** If this size of combined fields, or size of some file is exceeded, an
  267. `'error'` event is fired._
  268. ```js
  269. // The amount of bytes received for this form so far.
  270. form.bytesReceived;
  271. ```
  272. ```js
  273. // The expected number of bytes in this form.
  274. form.bytesExpected;
  275. ```
  276. ### .parse(request, callback)
  277. Parses an incoming Node.js `request` containing form data. If `callback` is
  278. provided, all fields and files are collected and passed to the callback.
  279. ```js
  280. const formidable = require('formidable');
  281. const form = formidable({ multiples: true, uploadDir: __dirname });
  282. form.parse(req, (err, fields, files) => {
  283. console.log('fields:', fields);
  284. console.log('files:', files);
  285. });
  286. ```
  287. You may overwrite this method if you are interested in directly accessing the
  288. multipart stream. Doing so will disable any `'field'` / `'file'` events
  289. processing which would occur otherwise, making you fully responsible for
  290. handling the processing.
  291. In the example below, we listen on couple of events and direct them to the
  292. `data` listener, so you can do whatever you choose there, based on whether its
  293. before the file been emitted, the header value, the header name, on field, on
  294. file and etc.
  295. Or the other way could be to just override the `form.onPart` as it's shown a bit
  296. later.
  297. ```js
  298. form.once('error', console.error);
  299. form.on('fileBegin', (filename, file) => {
  300. form.emit('data', { name: 'fileBegin', filename, value: file });
  301. });
  302. form.on('file', (filename, file) => {
  303. form.emit('data', { name: 'file', key: filename, value: file });
  304. });
  305. form.on('field', (fieldName, fieldValue) => {
  306. form.emit('data', { name: 'field', key: fieldName, value: fieldValue });
  307. });
  308. form.once('end', () => {
  309. console.log('Done!');
  310. });
  311. // If you want to customize whatever you want...
  312. form.on('data', ({ name, key, value, buffer, start, end, ...more }) => {
  313. if (name === 'partBegin') {
  314. }
  315. if (name === 'partData') {
  316. }
  317. if (name === 'headerField') {
  318. }
  319. if (name === 'headerValue') {
  320. }
  321. if (name === 'headerEnd') {
  322. }
  323. if (name === 'headersEnd') {
  324. }
  325. if (name === 'field') {
  326. console.log('field name:', key);
  327. console.log('field value:', value);
  328. }
  329. if (name === 'file') {
  330. console.log('file:', key, value);
  331. }
  332. if (name === 'fileBegin') {
  333. console.log('fileBegin:', key, value);
  334. }
  335. });
  336. ```
  337. ### .use(plugin: Plugin)
  338. A method that allows you to extend the Formidable library. By default we include
  339. 4 plugins, which esentially are adapters to plug the different built-in parsers.
  340. **The plugins added by this method are always enabled.**
  341. _See [src/plugins/](./src/plugins/) for more detailed look on default plugins._
  342. The `plugin` param has such signature:
  343. ```typescript
  344. function(formidable: Formidable, options: Options): void;
  345. ```
  346. The architecture is simple. The `plugin` is a function that is passed with the
  347. Formidable instance (the `form` across the README examples) and the options.
  348. **Note:** the plugin function's `this` context is also the same instance.
  349. ```js
  350. const formidable = require('formidable');
  351. const form = formidable({ keepExtensions: true });
  352. form.use((self, options) => {
  353. // self === this === form
  354. console.log('woohoo, custom plugin');
  355. // do your stuff; check `src/plugins` for inspiration
  356. });
  357. form.parse(req, (error, fields, files) => {
  358. console.log('done!');
  359. });
  360. ```
  361. **Important to note**, is that inside plugin `this.options`, `self.options` and
  362. `options` MAY or MAY NOT be the same. General best practice is to always use the
  363. `this`, so you can later test your plugin independently and more easily.
  364. If you want to disable some parsing capabilities of Formidable, you can disable
  365. the plugin which corresponds to the parser. For example, if you want to disable
  366. multipart parsing (so the [src/parsers/Multipart.js](./src/parsers/Multipart.js)
  367. which is used in [src/plugins/multipart.js](./src/plugins/multipart.js)), then
  368. you can remove it from the `options.enabledPlugins`, like so
  369. ```js
  370. const { Formidable } = require('formidable');
  371. const form = new Formidable({
  372. hash: 'sha1',
  373. enabledPlugins: ['octetstream', 'querystring', 'json'],
  374. });
  375. ```
  376. **Be aware** that the order _MAY_ be important too. The names corresponds 1:1 to
  377. files in [src/plugins/](./src/plugins) folder.
  378. Pull requests for new built-in plugins MAY be accepted - for example, more
  379. advanced querystring parser. Add your plugin as a new file in `src/plugins/`
  380. folder (lowercased) and follow how the other plugins are made.
  381. ### form.onPart
  382. If you want to use Formidable to only handle certain parts for you, you can do
  383. something similar. Or see
  384. [#387](https://github.com/node-formidable/node-formidable/issues/387) for
  385. inspiration, you can for example validate the mime-type.
  386. ```js
  387. const form = formidable();
  388. form.onPart = (part) => {
  389. part.on('data', (buffer) {
  390. // do whatever you want here
  391. });
  392. };
  393. ```
  394. For example, force Formidable to be used only on non-file "parts" (i.e., html
  395. fields)
  396. ```js
  397. const form = formidable();
  398. form.onPart = function(part) {
  399. // let formidable handle only non-file parts
  400. if (part.filename === '' || !part.mime) {
  401. // used internally, please do not override!
  402. form.handlePart(part);
  403. }
  404. };
  405. ```
  406. ### File
  407. ```ts
  408. export interface File {
  409. // The size of the uploaded file in bytes.
  410. // If the file is still being uploaded (see `'fileBegin'` event),
  411. // this property says how many bytes of the file have been written to disk yet.
  412. file.size: number;
  413. // The path this file is being written to. You can modify this in the `'fileBegin'` event in
  414. // case you are unhappy with the way formidable generates a temporary path for your files.
  415. file.path: string;
  416. // The name this file had according to the uploading client.
  417. file.name: string | null;
  418. // The mime type of this file, according to the uploading client.
  419. file.type: string | null;
  420. // A Date object (or `null`) containing the time this file was last written to.
  421. // Mostly here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
  422. file.lastModifiedDate: Date | null;
  423. // If `options.hash` calculation was set, you can read the hex digest out of this var.
  424. file.hash: string | 'sha1' | 'md5' | 'sha256' | null;
  425. }
  426. ```
  427. #### file.toJSON()
  428. This method returns a JSON-representation of the file, allowing you to
  429. `JSON.stringify()` the file which is useful for logging and responding to
  430. requests.
  431. ### Events
  432. #### `'progress'`
  433. Emitted after each incoming chunk of data that has been parsed. Can be used to
  434. roll your own progress bar.
  435. ```js
  436. form.on('progress', (bytesReceived, bytesExpected) => {});
  437. ```
  438. #### `'field'`
  439. Emitted whenever a field / value pair has been received.
  440. ```js
  441. form.on('field', (name, value) => {});
  442. ```
  443. #### `'fileBegin'`
  444. Emitted whenever a new file is detected in the upload stream. Use this event if
  445. you want to stream the file to somewhere else while buffering the upload on the
  446. file system.
  447. ```js
  448. form.on('fileBegin', (name, file) => {});
  449. ```
  450. #### `'file'`
  451. Emitted whenever a field / file pair has been received. `file` is an instance of
  452. `File`.
  453. ```js
  454. form.on('file', (name, file) => {});
  455. ```
  456. #### `'error'`
  457. Emitted when there is an error processing the incoming form. A request that
  458. experiences an error is automatically paused, you will have to manually call
  459. `request.resume()` if you want the request to continue firing `'data'` events.
  460. ```js
  461. form.on('error', (err) => {});
  462. ```
  463. #### `'aborted'`
  464. Emitted when the request was aborted by the user. Right now this can be due to a
  465. 'timeout' or 'close' event on the socket. After this event is emitted, an
  466. `error` event will follow. In the future there will be a separate 'timeout'
  467. event (needs a change in the node core).
  468. ```js
  469. form.on('aborted', () => {});
  470. ```
  471. #### `'end'`
  472. Emitted when the entire request has been received, and all contained files have
  473. finished flushing to disk. This is a great place for you to send your response.
  474. ```js
  475. form.on('end', () => {});
  476. ```
  477. ## Ports & Credits
  478. - [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++
  479. parser based on formidable
  480. - [Ryan Dahl](http://twitter.com/ryah) for his work on
  481. [http-parser](http://github.com/ry/http-parser) which heavily inspired the
  482. initial `multipart_parser.js`.
  483. ## Contributing
  484. If the documentation is unclear or has a typo, please click on the page's `Edit`
  485. button (pencil icon) and suggest a correction. If you would like to help us fix
  486. a bug or add a new feature, please check our
  487. [Contributing Guide](./CONTRIBUTING.md). Pull requests are welcome!
  488. Thanks goes to these wonderful people
  489. ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
  490. <!-- ALL-CONTRIBUTORS-LIST:START -->
  491. <!-- prettier-ignore-start -->
  492. <!-- markdownlint-disable -->
  493. <table>
  494. <tr>
  495. <td align="center"><a href="https://twitter.com/felixge"><img src="https://avatars3.githubusercontent.com/u/15000?s=460&v=4" width="100px;" alt=""/><br /><sub><b>Felix Geisendörfer</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/commits?author=felixge" title="Code">💻</a> <a href="#design-felixge" title="Design">🎨</a> <a href="#ideas-felixge" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=felixge" title="Documentation">📖</a></td>
  496. <td align="center"><a href="https://tunnckoCore.com"><img src="https://avatars3.githubusercontent.com/u/5038030?v=4" width="100px;" alt=""/><br /><sub><b>Charlike Mike Reagent</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/issues?q=author%3AtunnckoCore" title="Bug reports">🐛</a> <a href="#infra-tunnckoCore" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#design-tunnckoCore" title="Design">🎨</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=tunnckoCore" title="Code">💻</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=tunnckoCore" title="Documentation">📖</a> <a href="#example-tunnckoCore" title="Examples">💡</a> <a href="#ideas-tunnckoCore" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-tunnckoCore" title="Maintenance">🚧</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=tunnckoCore" title="Tests">⚠️</a></td>
  497. <td align="center"><a href="https://github.com/kedarv"><img src="https://avatars1.githubusercontent.com/u/1365665?v=4" width="100px;" alt=""/><br /><sub><b>Kedar</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/commits?author=kedarv" title="Code">💻</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=kedarv" title="Tests">⚠️</a> <a href="#question-kedarv" title="Answering Questions">💬</a> <a href="https://github.com/node-formidable/node-formidable/issues?q=author%3Akedarv" title="Bug reports">🐛</a></td>
  498. <td align="center"><a href="https://github.com/GrosSacASac"><img src="https://avatars0.githubusercontent.com/u/5721194?v=4" width="100px;" alt=""/><br /><sub><b>Walle Cyril</b></sub></a><br /><a href="#question-GrosSacASac" title="Answering Questions">💬</a> <a href="https://github.com/node-formidable/node-formidable/issues?q=author%3AGrosSacASac" title="Bug reports">🐛</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=GrosSacASac" title="Code">💻</a> <a href="#financial-GrosSacASac" title="Financial">💵</a> <a href="#ideas-GrosSacASac" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-GrosSacASac" title="Maintenance">🚧</a></td>
  499. <td align="center"><a href="https://github.com/xarguments"><img src="https://avatars2.githubusercontent.com/u/40522463?v=4" width="100px;" alt=""/><br /><sub><b>Xargs</b></sub></a><br /><a href="#question-xarguments" title="Answering Questions">💬</a> <a href="https://github.com/node-formidable/node-formidable/issues?q=author%3Axarguments" title="Bug reports">🐛</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=xarguments" title="Code">💻</a> <a href="#maintenance-xarguments" title="Maintenance">🚧</a></td>
  500. <td align="center"><a href="https://github.com/Amit-A"><img src="https://avatars1.githubusercontent.com/u/7987238?v=4" width="100px;" alt=""/><br /><sub><b>Amit-A</b></sub></a><br /><a href="#question-Amit-A" title="Answering Questions">💬</a> <a href="https://github.com/node-formidable/node-formidable/issues?q=author%3AAmit-A" title="Bug reports">🐛</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=Amit-A" title="Code">💻</a></td>
  501. </tr>
  502. <tr>
  503. <td align="center"><a href="https://charmander.me/"><img src="https://avatars1.githubusercontent.com/u/1889843?v=4" width="100px;" alt=""/><br /><sub><b>Charmander</b></sub></a><br /><a href="#question-charmander" title="Answering Questions">💬</a> <a href="https://github.com/node-formidable/node-formidable/issues?q=author%3Acharmander" title="Bug reports">🐛</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=charmander" title="Code">💻</a> <a href="#ideas-charmander" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-charmander" title="Maintenance">🚧</a></td>
  504. <td align="center"><a href="https://twitter.com/dylan_piercey"><img src="https://avatars2.githubusercontent.com/u/4985201?v=4" width="100px;" alt=""/><br /><sub><b>Dylan Piercey</b></sub></a><br /><a href="#ideas-DylanPiercey" title="Ideas, Planning, & Feedback">🤔</a></td>
  505. <td align="center"><a href="http://ochrona.jawne.info.pl"><img src="https://avatars1.githubusercontent.com/u/3618479?v=4" width="100px;" alt=""/><br /><sub><b>Adam Dobrawy</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/issues?q=author%3Aad-m" title="Bug reports">🐛</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=ad-m" title="Documentation">📖</a></td>
  506. <td align="center"><a href="https://github.com/amitrohatgi"><img src="https://avatars3.githubusercontent.com/u/12177021?v=4" width="100px;" alt=""/><br /><sub><b>amitrohatgi</b></sub></a><br /><a href="#ideas-amitrohatgi" title="Ideas, Planning, & Feedback">🤔</a></td>
  507. <td align="center"><a href="https://github.com/fengxinming"><img src="https://avatars2.githubusercontent.com/u/6262382?v=4" width="100px;" alt=""/><br /><sub><b>Jesse Feng</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/issues?q=author%3Afengxinming" title="Bug reports">🐛</a></td>
  508. <td align="center"><a href="https://qtmsheep.com"><img src="https://avatars1.githubusercontent.com/u/7271496?v=4" width="100px;" alt=""/><br /><sub><b>Nathanael Demacon</b></sub></a><br /><a href="#question-quantumsheep" title="Answering Questions">💬</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=quantumsheep" title="Code">💻</a> <a href="https://github.com/node-formidable/node-formidable/pulls?q=is%3Apr+reviewed-by%3Aquantumsheep" title="Reviewed Pull Requests">👀</a></td>
  509. </tr>
  510. <tr>
  511. <td align="center"><a href="https://github.com/MunMunMiao"><img src="https://avatars1.githubusercontent.com/u/18216142?v=4" width="100px;" alt=""/><br /><sub><b>MunMunMiao</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/issues?q=author%3AMunMunMiao" title="Bug reports">🐛</a></td>
  512. <td align="center"><a href="https://github.com/gabipetrovay"><img src="https://avatars0.githubusercontent.com/u/1170398?v=4" width="100px;" alt=""/><br /><sub><b>Gabriel Petrovay</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/issues?q=author%3Agabipetrovay" title="Bug reports">🐛</a> <a href="https://github.com/node-formidable/node-formidable/commits?author=gabipetrovay" title="Code">💻</a></td>
  513. <td align="center"><a href="https://github.com/Elzair"><img src="https://avatars0.githubusercontent.com/u/2352818?v=4" width="100px;" alt=""/><br /><sub><b>Philip Woods</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/commits?author=Elzair" title="Code">💻</a> <a href="#ideas-Elzair" title="Ideas, Planning, & Feedback">🤔</a></td>
  514. <td align="center"><a href="https://github.com/dmolim"><img src="https://avatars2.githubusercontent.com/u/7090374?v=4" width="100px;" alt=""/><br /><sub><b>Dmitry Ivonin</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/commits?author=dmolim" title="Documentation">📖</a></td>
  515. <td align="center"><a href="https://audiobox.fm"><img src="https://avatars1.githubusercontent.com/u/12844?v=4" width="100px;" alt=""/><br /><sub><b>Claudio Poli</b></sub></a><br /><a href="https://github.com/node-formidable/node-formidable/commits?author=masterkain" title="Code">💻</a></td>
  516. </tr>
  517. </table>
  518. <!-- markdownlint-enable -->
  519. <!-- prettier-ignore-end -->
  520. <!-- ALL-CONTRIBUTORS-LIST:END -->
  521. ## License
  522. Formidable is licensed under the [MIT License][license-url].
  523. <!-- badges -->
  524. <!-- prettier-ignore-start -->
  525. [codestyle-url]: https://github.com/airbnb/javascript
  526. [codestyle-img]: https://badgen.net/badge/code%20style/airbnb%20%2B%20prettier/ff5a5f?icon=airbnb&cache=300
  527. [codecov-url]: https://codecov.io/gh/node-formidable/node-formidable
  528. [codecov-img]: https://badgen.net/codecov/c/github/node-formidable/node-formidable/master?icon=codecov
  529. [npmv-canary-img]: https://badgen.net/npm/v/formidable/canary?icon=npm
  530. [npmv-dev-img]: https://badgen.net/npm/v/formidable/dev?icon=npm
  531. [npmv-img]: https://badgen.net/npm/v/formidable?icon=npm
  532. [npmv-url]: https://npmjs.com/package/formidable
  533. [license-img]: https://badgen.net/npm/license/formidable
  534. [license-url]: https://github.com/node-formidable/node-formidable/blob/master/LICENSE
  535. [chat-img]: https://badgen.net/badge/chat/on%20gitter/46BC99?icon=gitter
  536. [chat-url]: https://gitter.im/node-formidable/Lobby
  537. [libera-manifesto-url]: https://liberamanifesto.com
  538. [libera-manifesto-img]: https://badgen.net/badge/libera/manifesto/grey
  539. [renovateapp-url]: https://renovatebot.com
  540. [renovateapp-img]: https://badgen.net/badge/renovate/enabled/green?cache=300
  541. [prs-welcome-img]: https://badgen.net/badge/PRs/welcome/green?cache=300
  542. [prs-welcome-url]: http://makeapullrequest.com
  543. [twitter-url]: https://twitter.com/tunnckoCore
  544. [twitter-img]: https://badgen.net/twitter/follow/tunnckoCore?icon=twitter&color=1da1f2&cache=300
  545. [npm-weekly-img]: https://badgen.net/npm/dw/formidable?icon=npm&cache=300
  546. [npm-monthly-img]: https://badgen.net/npm/dm/formidable?icon=npm&cache=300
  547. [npm-yearly-img]: https://badgen.net/npm/dy/formidable?icon=npm&cache=300
  548. [npm-alltime-img]: https://badgen.net/npm/dt/formidable?icon=npm&cache=300&label=total%20downloads
  549. [nodejs-img]: https://badgen.net/badge/node/>=%2010.13/green?cache=300
  550. [ccommits-url]: https://conventionalcommits.org/
  551. [ccommits-img]: https://badgen.net/badge/conventional%20commits/v1.0.0/green?cache=300
  552. [contributing-url]: https://github.com/node-formidable/node-formidable/blob/master/CONTRIBUTING.md
  553. [code_of_conduct-url]: https://github.com/node-formidable/node-formidable/blob/master/CODE_OF_CONDUCT.md
  554. [open-issue-url]: https://github.com/node-formidable/node-formidable/issues/new
  555. [tidelift-url]: https://tidelift.com/subscription/pkg/npm-formidable?utm_source=npm-formidable&utm_medium=referral&utm_campaign=enterprise
  556. [tidelift-img]: https://badgen.net/badge/tidelift/subscription/4B5168?labelColor=F6914D
  557. [kofi-url]: https://ko-fi.com/tunnckoCore/commissions
  558. [kofi-img]: https://badgen.net/badge/ko-fi/support/29abe0c2?cache=300&icon=https://rawcdn.githack.com/tunnckoCore/badgen-icons/f8264c6414e0bec449dd86f2241d50a9b89a1203/icons/kofi.svg
  559. [linux-build-img]: https://badgen-net.charlike.now.sh/github/checks/node-formidable/node-formidable?label=linux%20build&icon=github
  560. [macos-build-img]: https://badgen-net.charlike.now.sh/github/checks/node-formidable/node-formidable?label=macos%20build&icon=github
  561. [windows-build-img]: https://badgen-net.charlike.now.sh/github/checks/node-formidable/node-formidable?label=windows%20build&icon=github
  562. [build-url]: https://github.com/node-formidable/node-formidable/actions?query=workflow%3Anodejs
  563. <!-- prettier-ignore-end -->