[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: monthly\n\n  - package-ecosystem: npm\n    directory: /\n    schedule:\n      interval: monthly\n    open-pull-requests-limit: 10\n    ignore:\n      - dependency-name: \"*\"\n        update-types: [\"version-update:semver-major\"]\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\n\non:\n- pull_request\n- push\n\npermissions:\n  contents: read\n\njobs:\n  test:\n    permissions:\n      checks: write  # for coverallsapp/github-action to create new checks\n      contents: read  # for actions/checkout to fetch code\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        name:\n        - Node.js 10.x\n        - Node.js 11.x\n        - Node.js 12.x\n        - Node.js 13.x\n        - Node.js 14.x\n        - Node.js 15.x\n        - Node.js 16.x\n        - Node.js 17.x\n        - Node.js 18.x\n        - Node.js 19.x\n        - Node.js 20.x\n        - Node.js 21.x\n        - Node.js 22.x\n        - Node.js 23.x\n        - Node.js 24.x\n        - Node.js 25.x\n\n        include:\n        - name: Node.js 10.x\n          node-version: \"10.24\"\n          npm-i: mocha@8.4.0\n          \n        - name: Node.js 11.x\n          node-version: \"11.15\"\n          npm-i: mocha@8.4.0\n          \n        - name: Node.js 12.x\n          node-version: \"12.22\"\n          npm-i: mocha@9.2.2\n          \n        - name: Node.js 13.x\n          node-version: \"13.14\"\n          npm-i: mocha@9.2.2\n          \n        - name: Node.js 14.x\n          node-version: \"14.21\"\n          npm-i: mocha@9.2.2\n          \n        - name: Node.js 15.x\n          node-version: \"15.14\"\n          npm-i: mocha@9.2.2\n          \n        - name: Node.js 16.x\n          node-version: \"16.20\"\n          \n        - name: Node.js 17.x\n          node-version: \"17.9\"\n          \n        - name: Node.js 18.x\n          node-version: \"18.18\"\n          \n        - name: Node.js 19.x\n          node-version: \"19.9\"\n          \n        - name: Node.js 20.x\n          node-version: \"20.9\"\n          \n        - name: Node.js 21.x\n          node-version: \"21\"\n          \n        - name: Node.js 22.x\n          node-version: \"22\"\n\n        - name: Node.js 23.x\n          node-version: \"23\"\n\n        - name: Node.js 24.x\n          node-version: \"24\"\n\n        - name: Node.js 25.x\n          node-version: \"25\"\n\n    steps:\n    - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n    - name: Install Node.js ${{ matrix.node-version }}\n      shell: bash -eo pipefail -l {0}\n      run: |\n        nvm install --default ${{ matrix.node-version }}\n        if [[ \"${{ matrix.node-version }}\" == 0.* && \"$(cut -d. -f2 <<< \"${{ matrix.node-version }}\")\" -lt 10 ]]; then\n          nvm install --alias=npm 0.10\n          nvm use ${{ matrix.node-version }}\n          if [[ \"$(npm -v)\" == 1.1.* ]]; then\n            nvm exec npm npm install -g npm@1.1\n            ln -fs \"$(which npm)\" \"$(dirname \"$(nvm which npm)\")/npm\"\n          else\n            sed -i '1s;^.*$;'\"$(printf '#!%q' \"$(nvm which npm)\")\"';' \"$(readlink -f \"$(which npm)\")\"\n          fi\n          npm config set strict-ssl false\n        fi\n        dirname \"$(nvm which ${{ matrix.node-version }})\" >> \"$GITHUB_PATH\"\n\n    - name: Remove npm module(s) ${{ matrix.npm-rm }}\n      run: npm rm --silent --save-dev ${{ matrix.npm-rm }}\n      if: matrix.npm-rm != ''\n\n    - name: Install npm module(s) ${{ matrix.npm-i }}\n      run: npm install --save-dev ${{ matrix.npm-i }}\n      if: matrix.npm-i != ''\n\n    - name: Install Node.js dependencies\n      run: npm install\n\n    - name: List environment\n      id: list_env\n      shell: bash\n      run: |\n        echo \"node@$(node -v)\"\n        echo \"npm@$(npm -v)\"\n        npm -s ls ||:\n        (npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 \"=\" $3 }' >> \"$GITHUB_OUTPUT\"\n\n    - name: Lint code\n      run: npm run lint\n\n    - name: Run tests\n      shell: bash\n      run: |\n        if npm -ps ls nyc | grep -q nyc; then\n          npm run test-ci\n        else\n          npm test\n        fi\n\n    - name: Collect code coverage\n      uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # master\n      if: steps.list_env.outputs.nyc != ''\n      with:\n        github-token: ${{ secrets.GITHUB_TOKEN }}\n        flag-name: run-${{ matrix.test_number }}\n        parallel: true\n\n  coverage:\n    permissions:\n      checks: write  # for coverallsapp/github-action to create new checks\n    needs: test\n    runs-on: ubuntu-latest\n    steps:\n    - name: Upload code coverage\n      uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # master\n      with:\n        github-token: ${{ secrets.GITHUB_TOKEN }}\n        parallel-finished: true\n"
  },
  {
    "path": ".github/workflows/codeql.yml",
    "content": "# For most projects, this workflow file will not need changing; you simply need\n# to commit it to your repository.\n#\n# You may wish to alter this file to override the set of languages analyzed,\n# or to provide custom queries or build logic.\n#\n# ******** NOTE ********\n# We have attempted to detect the languages in your repository. Please check\n# the `language` matrix defined below to confirm you have the correct set of\n# supported CodeQL languages.\n#\nname: \"CodeQL\"\n\non:\n  push:\n    branches: [\"main\", \"v2\"]\n  pull_request:\n    # The branches below must be a subset of the branches above\n    branches: [\"main\", \"v2\"]\n  schedule:\n    - cron: \"0 0 * * 1\"\n\npermissions:\n  contents: read\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n    permissions:\n      actions: read\n      contents: read\n      security-events: write\n\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      # Initializes the CodeQL tools for scanning.\n      - name: Initialize CodeQL\n        uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4\n        with:\n          languages: javascript\n          # If you wish to specify custom queries, you can do so here or in a config file.\n          # By default, queries listed here will override any specified in a config file.\n          # Prefix the list here with \"+\" to use these queries and those in the config file.\n\n      # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).\n      # If this step fails, then you should remove it and run the build manually (see below)\n      # - name: Autobuild\n      #   uses: github/codeql-action/autobuild@3ab4101902695724f9365a384f86c1074d94e18c # v3.24.7\n\n      # ℹ️ Command-line programs to run using the OS shell.\n      # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun\n\n      #   If the Autobuild fails above, remove it and uncomment the following three lines.\n      #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.\n\n      # - run: |\n      #   echo \"Run, Build Application using script\"\n      #   ./location_of_script_within_repo/buildscript.sh\n\n      - name: Perform CodeQL Analysis\n        uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4\n        with:\n          category: \"/language:javascript\"\n"
  },
  {
    "path": ".github/workflows/scorecard.yml",
    "content": "# This workflow uses actions that are not certified by GitHub. They are provided\n# by a third-party and are governed by separate terms of service, privacy\n# policy, and support documentation.\n\nname: Scorecard supply-chain security\n\non:\n  # For Branch-Protection check. Only the default branch is supported. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection\n  branch_protection_rule:\n  # To guarantee Maintained check is occasionally updated. See\n  # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained\n  schedule:\n    - cron: '16 21 * * 1'\n  push:\n    branches: [ \"main\" ]\n\n# Declare default permissions as read only.\npermissions: read-all\n\njobs:\n  analysis:\n    name: Scorecard analysis\n    runs-on: ubuntu-latest\n    permissions:\n      # Needed to upload the results to code-scanning dashboard.\n      security-events: write\n      # Needed to publish results and get a badge (see publish_results below).\n      id-token: write\n      # Uncomment the permissions below if installing in a private repository.\n      # contents: read\n      # actions: read\n\n    steps:\n      - name: \"Checkout code\"\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          persist-credentials: false\n\n      - name: \"Run analysis\"\n        uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3\n        with:\n          results_file: results.sarif\n          results_format: sarif\n          # (Optional) \"write\" PAT token. Uncomment the `repo_token` line below if:\n          # - you want to enable the Branch-Protection check on a *public* repository, or\n          # - you are installing Scorecard on a *private* repository\n          # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.\n          # repo_token: ${{ secrets.SCORECARD_TOKEN }}\n\n          # Public repositories:\n          #   - Publish results to OpenSSF REST API for easy access by consumers\n          #   - Allows the repository to include the Scorecard badge.\n          #   - See https://github.com/ossf/scorecard-action#publishing-results.\n          # For private repositories:\n          #   - `publish_results` will always be set to `false`, regardless\n          #     of the value entered here.\n          publish_results: true\n\n      # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF\n      # format to the repository Actions tab.\n      - name: \"Upload artifact\"\n        uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0\n        with:\n          name: SARIF file\n          path: results.sarif\n          retention-days: 5\n\n      # Upload the results to GitHub's code scanning dashboard.\n      - name: \"Upload to code-scanning\"\n        uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4.32.4\n        with:\n          sarif_file: results.sarif\n          \n"
  },
  {
    "path": ".gitignore",
    "content": "# OS X\n.DS_Store*\nIcon?\n._*\n\n# Windows\nThumbs.db\nehthumbs.db\nDesktop.ini\n\n# Linux\n.directory\n*~\n\n\n# npm\nnode_modules\n*.log\n*.gz\n\n\n# Coverage & Test Output\ncoverage\n.nyc_output\n"
  },
  {
    "path": ".npmrc",
    "content": "package-lock=false\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Change log\n\nAll notable changes to this project will be documented in this file.\nThis project adheres to [Semantic Versioning](http://semver.org/).\n\n## 2.1.1\n\n- Fix [CVE-2026-3520](https://www.cve.org/CVERecord?id=CVE-2026-3520) ([GHSA-5528-5vmv-3xc2](https://github.com/expressjs/multer/security/advisories/GHSA-5528-5vmv-3xc2))\n- fix error/abort handling\n\n## 2.1.0\n\n- Add `defParamCharset` option for UTF-8 filename support ([#1210](https://github.com/expressjs/multer/pull/1210))\n- Fix [CVE-2026-2359](https://www.cve.org/CVERecord?id=CVE-2026-2359) ([GHSA-v52c-386h-88mc](https://github.com/expressjs/multer/security/advisories/GHSA-v52c-386h-88mc))\n- Fix [CVE-2026-3304](https://www.cve.org/CVERecord?id=CVE-2026-3304) ([GHSA-xf7r-hgr6-v32p](https://github.com/expressjs/multer/security/advisories/GHSA-xf7r-hgr6-v32p))\n\n\n## 2.0.2\n\n- Fix [CVE-2025-7338](https://www.cve.org/CVERecord?id=CVE-2025-7338) ([GHSA-fjgf-rc76-4x9p](https://github.com/expressjs/multer/security/advisories/GHSA-fjgf-rc76-4x9p))\n\n\n## 2.0.1\n\n- Fix [CVE-2025-48997](https://www.cve.org/CVERecord?id=CVE-2025-48997) ([GHSA-g5hg-p3ph-g8qg](https://github.com/expressjs/multer/security/advisories/GHSA-g5hg-p3ph-g8qg))\n\n## 2.0.0\n\n- **Breaking change: The minimum supported Node version is now 10.16.0**\n- Fix [CVE-2025-47935](https://www.cve.org/CVERecord?id=CVE-2025-47935) ([GHSA-44fp-w29j-9vj5](https://github.com/expressjs/multer/security/advisories/GHSA-44fp-w29j-9vj5))\n- Fix [CVE-2025-47944](https://www.cve.org/CVERecord?id=CVE-2025-47944) ([GHSA-4pg4-qvpc-4q3h](https://github.com/expressjs/multer/security/advisories/GHSA-4pg4-qvpc-4q3h))\n\n## 1.4.5-lts.2\n\n- Fix out-of-band error event from busboy (#1177)\n\n## 1.4.5-lts.1\n\n- No changes\n\n## 1.4.4-lts.1\n\n- Bugfix: Bump busboy to fix CVE-2022-24434 (#1097)\n- Breaking: Require Node.js 10.16.0 or later (#1097)\n\n## 1.4.4 - 2021-12-07\n\n- Bugfix: Handle missing field names (#913)\n- Docs: Add Vietnamese translation (#803)\n- Docs: Improve Spanish translation (#948)\n\n## 1.4.3 - 2021-08-09\n\n- Bugfix: Avoid deprecated pseudoRandomBytes function (#774)\n- Docs: Add Português Brazil translation for README (#758)\n- Docs: Clarify the callback calling convention (#775)\n- Docs: Add example on how to link to html multipart form (#580)\n- Docs: Add Spanish translation for README (#838)\n- Docs: Add Math.random() to storage filename example (#841)\n- Docs: Fix mistakes in russian doc (#869)\n- Docs: Improve Português Brazil translation (#877)\n- Docs: Update var to const in all Readmes (#1024)\n- Internal: Bump mkdirp version (#862)\n- Internal: Bump Standard version (#878)\n\n## 1.4.2 - 2019-07-16\n\n- Docs: Add Russian translation for README (#662)\n- Docs: Patch zh-CN README base on newest README (#670)\n- Docs: Fix broken link in Readme (#679)\n- Docs: Fix broken link in Chinese Readme (#730)\n- Docs: Fix typo in Russian README (#738)\n- Docs: Add unit for fieldSize in busboy limit params (#734)\n- Internal: Make unit tests comaptible with Node.js 13.x (#752)\n\n## 1.4.1 - 2018-10-11\n\n- Bugfix: Make sure that req.file.buffer always is a Buffer\n\n## 1.4.0 - 2018-09-26\n\n- Feature: Make Multer errors inherit from MulterError\n\n## 1.3.1 - 2018-06-28\n\n- Bugfix: Bump vulnerable dependency\n\n## 1.3.0 - 2017-01-25\n\n- Feature: Expose preservePath option\n\n## 1.2.1 - 2016-12-14\n\n- Bugfix: Prevent Multiple Errors from Crashing\n\n## 1.2.0 - 2016-08-04\n\n- Feature: add .none() for accepting only fields\n\n## 1.1.0 - 2015-10-23\n\n- Feature: accept any file, regardless of fieldname\n\n## 1.0.6 - 2015-10-03\n\n- Bugfix: always report limit errors\n\n## 1.0.5 - 2015-09-19\n\n- Bugfix: drain the stream before considering request done\n\n## 1.0.4 - 2015-09-19\n\n- Bugfix: propagate all errors from busboy\n\n## 1.0.3 - 2015-08-06\n\n- Bugfix: ensure file order is correct\n\n## 1.0.2 - 2015-08-06\n\n- Bugfix: don't hang when hitting size limit\n\n## 1.0.1 - 2015-07-20\n\n- Bugfix: decrement pending writes on error\n\n## 1.0.0 - 2015-07-18\n\n- Introduce storage engines\n- Specify expected fields\n- Follow the W3C JSON form spec\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2014 Hage Yaapa <[http://www.hacksparrow.com](http://www.hacksparrow.com)>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter is a node.js middleware for handling `multipart/form-data`, which is primarily used for uploading files. It is written\non top of [busboy](https://github.com/mscdex/busboy) for maximum efficiency.\n\n**NOTE**: Multer will not process any form which is not multipart (`multipart/form-data`).\n\n## Translations\n\nThis README is also available in other languages:\n\n|                                                                                |                 |\n| ------------------------------------------------------------------------------ | --------------- |\n| [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md)      | Arabic          |\n| [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md)  | Chinese         |\n| [Français](https://github.com/expressjs/multer/blob/main/doc/README-fr.md)     | French          |\n| [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md)       | Korean          |\n| [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) | Portuguese (BR) |\n| [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) | Russian         |\n| [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md)      | Spanish         |\n| [O'zbek tili](https://github.com/expressjs/multer/blob/main/doc/README-uz.md)  | Uzbek           |\n| [Việt Nam](https://github.com/expressjs/multer/blob/main/doc/README-vi.md)     | Vietnamese      |\n| [Türkçe](https://github.com/expressjs/multer/blob/main/doc/README-tr.md)       | Turkish         |\n\n\n## Installation\n\n```sh\n$ npm install multer\n```\n\n## Usage\n\nMulter adds a `body` object and a `file` or `files` object to the `request` object. The `body` object contains the values of the text fields of the form, the `file` or `files` object contains the files uploaded via the form.\n\nBasic usage example:\n\nDon't forget the `enctype=\"multipart/form-data\"` in your form.\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nconst express = require('express')\nconst multer  = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file is the `avatar` file\n  // req.body will hold the text fields, if there were any\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files is array of `photos` files\n  // req.body will contain the text fields, if there were any\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files is an object (String -> Array) where fieldname is the key, and the value is array of files\n  //\n  // e.g.\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // req.body will contain the text fields, if there were any\n})\n```\n\nIn case you need to handle a text-only multipart form, you should use the `.none()` method:\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body contains the text fields\n})\n```\n\nHere's an example on how multer is used in a HTML form. Take special note of the `enctype=\"multipart/form-data\"` and `name=\"uploaded_file\"` fields:\n\n```html\n<form action=\"/stats\" enctype=\"multipart/form-data\" method=\"post\">\n  <div class=\"form-group\">\n    <input type=\"file\" class=\"form-control-file\" name=\"uploaded_file\">\n    <input type=\"text\" class=\"form-control\" placeholder=\"Number of speakers\" name=\"nspeakers\">\n    <input type=\"submit\" value=\"Get me the stats!\" class=\"btn btn-default\">\n  </div>\n</form>\n```\n\nThen in your javascript file you would add these lines to access both the file and the body. It is important that you use the `name` field value from the form in your upload function. This tells multer which field on the request it should look for the files in. If these fields aren't the same in the HTML form and on your server, your upload will fail:\n\n```javascript\nconst multer  = require('multer')\nconst upload = multer({ dest: './public/data/uploads/' })\napp.post('/stats', upload.single('uploaded_file'), function (req, res) {\n  // req.file is the name of your file in the form above, here 'uploaded_file'\n  // req.body will hold the text fields, if there were any\n  console.log(req.file, req.body)\n});\n```\n\n\n\n## API\n\n### File information\n\nEach file contains the following information:\n\nKey | Description | Note\n--- | --- | ---\n`fieldname` | Field name specified in the form |\n`originalname` | Name of the file on the user's computer |\n`encoding` | Encoding type of the file |\n`mimetype` | Mime type of the file |\n`size` | Size of the file in bytes |\n`destination` | The folder to which the file has been saved | `DiskStorage`\n`filename` | The name of the file within the `destination` | `DiskStorage`\n`path` | The full path to the uploaded file | `DiskStorage`\n`buffer` | A `Buffer` of the entire file | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter accepts an options object, the most basic of which is the `dest`\nproperty, which tells Multer where to upload the files. In case you omit the\noptions object, the files will be kept in memory and never written to disk.\n\nBy default, Multer will rename the files so as to avoid naming conflicts. The\nrenaming function can be customized according to your needs.\n\nThe following are the options that can be passed to Multer.\n\nKey | Description\n--- | ---\n`dest` or `storage` | Where to store the files\n`fileFilter` | Function to control which files are accepted\n`limits` | Limits of the uploaded data\n`preservePath` | Keep the full path of files instead of just the base name\n`defParamCharset` | Default character set to use for values of part header parameters (e.g. filename) that are not extended parameters (that contain an explicit charset). Default: `'latin1'`\n\nIn an average web app, only `dest` might be required, and configured as shown in\nthe following example.\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\n\nIf you want more control over your uploads, you'll want to use the `storage`\noption instead of `dest`. Multer ships with storage engines `DiskStorage`\nand `MemoryStorage`; More engines are available from third parties.\n\n#### `.single(fieldname)`\n\nAccept a single file with the name `fieldname`. The single file will be stored\nin `req.file`.\n\n#### `.array(fieldname[, maxCount])`\n\nAccept an array of files, all with the name `fieldname`. Optionally error out if\nmore than `maxCount` files are uploaded. The array of files will be stored in\n`req.files`.\n\n#### `.fields(fields)`\n\nAccept a mix of files, specified by `fields`. An object with arrays of files\nwill be stored in `req.files`.\n\n`fields` should be an array of objects with `name` and optionally a `maxCount`.\nExample:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\nAccept only text fields. If any file upload is made, error with code\n\"LIMIT\\_UNEXPECTED\\_FILE\" will be issued.\n\n#### `.any()`\n\nAccepts all files that comes over the wire. An array of files will be stored in\n`req.files`.\n\n**WARNING:** Make sure that you always handle the files that a user uploads.\nNever add multer as a global middleware since a malicious user could upload\nfiles to a route that you didn't anticipate. Only use this function on routes\nwhere you are handling the uploaded files.\n\n### `storage`\n\n#### `DiskStorage`\n\nThe disk storage engine gives you full control on storing files to disk.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)\n    cb(null, file.fieldname + '-' + uniqueSuffix)\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\nThere are two options available, `destination` and `filename`. They are both\nfunctions that determine where the file should be stored.\n\n`destination` is used to determine within which folder the uploaded files should\nbe stored. This can also be given as a `string` (e.g. `'/tmp/uploads'`). If no\n`destination` is given, the operating system's default directory for temporary\nfiles is used.\n\n**Note:** You are responsible for creating the directory when providing\n`destination` as a function. When passing a string, multer will make sure that\nthe directory is created for you.\n\n`filename` is used to determine what the file should be named inside the folder.\nIf no `filename` is given, each file will be given a random name that doesn't\ninclude any file extension.\n\n**Note:** Multer will not append any file extension for you, your function\nshould return a filename complete with a file extension.\n\nEach function gets passed both the request (`req`) and some information about\nthe file (`file`) to aid with the decision.\n\nNote that `req.body` might not have been fully populated yet. It depends on the\norder that the client transmits fields and files to the server.\n\nFor understanding the calling convention used in the callback (needing to pass\nnull as the first param), refer to\n[Node.js error handling](https://web.archive.org/web/20220417042018/https://www.joyent.com/node-js/production/design/errors)\n\n#### `MemoryStorage`\n\nThe memory storage engine stores the files in memory as `Buffer` objects. It\ndoesn't have any options.\n\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\n\nWhen using memory storage, the file info will contain a field called\n`buffer` that contains the entire file.\n\n**WARNING**: Uploading very large files, or relatively small files in large\nnumbers very quickly, can cause your application to run out of memory when\nmemory storage is used.\n\n### `limits`\n\nAn object specifying the size limits of the following optional properties. Multer passes this object into busboy directly, and the details of the properties can be found on [busboy's page](https://github.com/mscdex/busboy#busboy-methods).\n\nThe following integer values are available:\n\nKey | Description | Default\n--- | --- | ---\n`fieldNameSize` | Max field name size | 100 bytes\n`fieldSize` | Max field value size (in bytes) | 1MB\n`fields` | Max number of non-file fields | Infinity\n`fileSize` | For multipart forms, the max file size (in bytes) | Infinity\n`files` | For multipart forms, the max number of file fields | Infinity\n`parts` | For multipart forms, the max number of parts (fields + files) | Infinity\n`headerPairs` | For multipart forms, the max number of header key=>value pairs to parse | 2000\n\nSpecifying the limits can help protect your site against denial of service (DoS) attacks.\n\n### `fileFilter`\n\nSet this to a function to control which files should be uploaded and which\nshould be skipped. The function should look like this:\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // The function should call `cb` with a boolean\n  // to indicate if the file should be accepted\n\n  // To reject this file pass `false`, like so:\n  cb(null, false)\n\n  // To accept the file pass `true`, like so:\n  cb(null, true)\n\n  // You can always pass an error if something goes wrong:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n## Error handling\n\nWhen encountering an error, Multer will delegate the error to Express. You can\ndisplay a nice error page using [the standard express way](http://expressjs.com/guide/error-handling.html).\n\nIf you want to catch errors specifically from Multer, you can call the\nmiddleware function by yourself. Also, if you want to catch only [the Multer errors](https://github.com/expressjs/multer/blob/main/lib/multer-error.js), you can use the `MulterError` class that is attached to the `multer` object itself (e.g. `err instanceof multer.MulterError`).\n\n```javascript\nconst multer = require('multer')\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // A Multer error occurred when uploading.\n    } else if (err) {\n      // An unknown error occurred when uploading.\n    }\n\n    // Everything went fine.\n  })\n})\n```\n\n## Custom storage engine\n\nFor information on how to build your own storage engine, see [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## License\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "StorageEngine.md",
    "content": "# Multer Storage Engine\n\nStorage engines are classes that expose two functions: `_handleFile` and `_removeFile`.\nFollow the template below to get started with your own custom storage engine.\n\nWhen asking the user for input (such as where to save this file), always give\nthem the parameters `req, file, cb`, in this order. This makes it easier for\ndevelopers to switch between storage engines.\n\nFor example, in the template below, the engine saves the files to the disk. The\nuser tells the engine where to save the file, and this is done by\nproviding the `destination` parameter:\n\n```javascript\nvar storage = myCustomStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/var/www/uploads/' + file.originalname)\n  }\n})\n```\n\nYour engine is responsible for storing the file and returning information on how to\naccess the file in the future. This is done by the `_handleFile` function.\n\nThe file data will be given to you as a stream (`file.stream`). You should pipe\nthis data somewhere, and when you are done, call `cb` with some information on the\nfile.\n\nThe information you provide in the callback will be merged with multer's file object,\nand then presented to the user via `req.files`.\n\nYour engine is also responsible for removing files if an error is encountered\nlater on. Multer will decide which files to delete and when. Your storage class must\nimplement the `_removeFile` function. It will receive the same arguments as\n`_handleFile`. Invoke the callback once the file has been removed.\n\n## Template\n\n```javascript\nvar fs = require('fs')\n\nfunction getDestination (req, file, cb) {\n  cb(null, '/dev/null')\n}\n\nfunction MyCustomStorage (opts) {\n  this.getDestination = (opts.destination || getDestination)\n}\n\nMyCustomStorage.prototype._handleFile = function _handleFile (req, file, cb) {\n  this.getDestination(req, file, function (err, path) {\n    if (err) return cb(err)\n\n    var outStream = fs.createWriteStream(path)\n\n    file.stream.pipe(outStream)\n    outStream.on('error', cb)\n    outStream.on('finish', function () {\n      cb(null, {\n        path: path,\n        size: outStream.bytesWritten\n      })\n    })\n  })\n}\n\nMyCustomStorage.prototype._removeFile = function _removeFile (req, file, cb) {\n  fs.unlink(file.path, cb)\n}\n\nmodule.exports = function (opts) {\n  return new MyCustomStorage(opts)\n}\n```\n"
  },
  {
    "path": "doc/README-ar.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\n<div dir=\"rtl\">\n\nتعتبر Multer وسيط node.js لمعالجة `multipart/form-data`, والتي تُستخدم أساسًا لتحميل الملفات. تم بناء هذا الوسيط بالإعتماد على [busboy](https://github.com/mscdex/busboy) لأجل الحصول على أقصى قدر من الكفاءة.\n\n**ملاحظة**: لن يقوم Multer بمعالجة أي شكل غير متعدد الأجزاء (`multipart/form-data`).\n\n\n## الترجمات\n\nهذا الملف متاح أيضًا بلغات أخرى:\n\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (الإنجليزية)\n- [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) (الإسبانية)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (الصينية)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (الكورية)\n- [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (الروسية)\n- [Việt Nam](https://github.com/expressjs/multer/blob/main/doc/README-vi.md) (الفتنامية)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (البرتغالية)\n\n\n## التنصيب\n\n</div>\n\n```sh\n$ npm install --save multer\n```\n\n<div dir=\"rtl\">\n\n## الاستعمال\n\nيضيف Multer كائن `body` وكائن `file` أو `files` إلى كائن `request`. يحتوي الكائن `body` على قيم مدخلات النص في الإستمارة ، بينما يحتوي الكائن `file` أو `files` على الملفات التي تم تحميلها عبر الإستمارة.\n\nمثال على الاستخدام الأساسي:\n\nلا تنسَ <span dir=\"ltr\"> `enctype=\"multipart/form-data\"` </span> في الإستمارة الخاص بك.\n\n<div dir=\"ltr\">\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nvar express = require('express')\nvar multer  = require('multer')\nvar upload = multer({ dest: 'uploads/' })\n\nvar app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file is the `avatar` file\n  // req.body will hold the text fields, if there were any\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files is array of `photos` files\n  // req.body will contain the text fields, if there were any\n})\n\nvar uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files is an object (String -> Array) where fieldname is the key, and the value is array of files\n  //\n  // e.g.\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // req.body will contain the text fields, if there were any\n})\n```\n\n</div>\n\nإذا احتجت لمعالجة إستمارة متعددة الأجزاء للنص فقط ، فيجب عليك استخدام الدالة `.none ()`:\n\n<div dir=\"ltr\">\n\n```javascript\nvar express = require('express')\nvar app = express()\nvar multer  = require('multer')\nvar upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body contains the text fields\n})\n```\n</div>\n\n## واجهة برمجة التطبيقات (API)\n\n### معلومات الملف\n\nكل ملف يحتوي على المعلومات التالية:\n\nمفتاح | وصف | ملاحظة\n--- | --- | ---\n`fieldname` | اسم المُدخَل المحدد في الإستمارة |\n`originalname` | اسم الملف على كمبيوتر المستخدم |\n`encoding` | نوع تشفير الملف |\n`mimetype` | نوع  ملف ملحقات بريد إنترنت متعددة الأغراض ( MIME ) |\n`size` | حجم الملف بالبايت |\n`destination` | المجلد الذي تم حفظ الملف إليه | `تخزين على الاسطوانة` (`DiskStorage`)\n`filename` | اسم الملف داخل \"الوجهة\" ( `destination` ) | `تخزين على الاسطوانة` (`DiskStorage`)\n`path` | المسار الكامل للملف الذي تم تحميله | `تخزين على الاسطوانة` (`DiskStorage`)\n`buffer` | \"ذاكرة\" (`Buffer`) للملف بأكمله | `تخزين على الذاكرة ` (`MemoryStorage`)\n\n\n### `multer(opts)`\n\nيقبل Multer كائن الخيارات ، وأهمها خاصية `dest`، والتي تحدد مكان تحميل الملفات. في حال حذفت كائن الخيارات ، سيتم الاحتفاظ بالملفات في الذاكرة ولن تتم كتابتها مطلقًا على القرص.\n\nبشكل افتراضي ، سيقوم Multer بإعادة تسمية الملفات لتجنب تعارض الأسماء. يمكن تخصيص وظيفة إعادة التسمية وفقا لاحتياجاتك.\n\nفيما يلي الخيارات التي يمكن تمريرها إلى Multer:\n\nمفتاح | وصف\n--- | ---\n`dest` أو `storage` | مكان لتخزين الملفات\n`fileFilter` | دالة للسيطرة على الملفات التي يتم قبولها\n`limits` | حدود البيانات التي تم تحميلها\n`preservePath` | الاحتفظ بالمسار الكامل للملفات بدلاً من الاسم الأساسي\n`defParamCharset` | مجموعة الأحرف الافتراضية لاستخدامها لقيم معاملات رأس الجزء (مثل اسم الملف) التي ليست معاملات موسعة (لا تحتوي على مجموعة أحرف صريحة). الافتراضي: `'latin1'`\n\nفي تطبيق ويب متوسط  ​​، قد تكون هناك حاجة  فقط إلى `dest`، وتكوينها كما هو موضح في\nالمثال التالي :\n\n<div dir='ltr'>\n\n```javascript\nvar upload = multer({ dest: 'uploads/' })\n```\n\n</div>\n\nإذا كنت تريد مزيدًا من التحكم في عمليات التحميل ، فستحتاج إلى استخدام خيار `storage` بدلاً من `dest`. يأتي Multer مع محركات التخزين `DiskStorage` و` MemoryStorage` ؛ كما تتوفر المزيد من المحركات من أطراف ثالثة.\n\n#### `.single(fieldname)`\n\nقبول ملف واحد باسم `اسم-المُدخَل`. سيتم تخزين الملف في `req.file`.\n\n#### `.array(fieldname[, maxCount])`\n\nقبول مصفوفة من الملفات ، وكلها تحمل اسم `اسم-المُدخَل`. يظهر خطأ اختياريً إذا تم تحميل ملفات أكثر من `maxCount`. سيتم تخزين مصفوفة الملفات في `req.files`.\n\n#### `.fields(fields)`\n\nقبول مزيج من الملفات ، المحدد بواسطة `المدخلات`. سيتم تخزين كائن مع مصفوفات من الملفات في `req.files`.\n\nيجب أن تكون `المدخلات` عبارة عن مصفوفة من الكائنات التي توفر بشكل اساسي `name` واختيارياً `maxCount`.\nمثال:\n\n<div dir='ltr'>\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n</div>\n\n#### `.none()`\n\nقبول المدخلات النصية فقط. في حالة رفع أي ملف ، سيتم إصدار خطأ بشيفرة \"LIMIT \\_UNEXPECTED \\_FILE\".\n\n#### `.any()`\n\nقبول جميع الملفات التي تأتي عبر السلك. سيتم تخزين مصفوفة من الملفات في `req.files`.\n\n**تحذير:** تأكد من أنك تعالج دائمًا الملفات التي يقوم المستخدم بتحميلها. لا تقم أبداً بإضافة multer باعتبارها أداة وسيطة عامة ، حيث يمكن للمستخدم الضار تحميل الملفات إلى مسار غير متتوقع. استخدم هذه الدالة فقط على المسارات التي تتعامل فيها مع الملفات التي تم تحميلها.\n\n### `storage`\n\n#### `DiskStorage`\n\nيمنحك محرك تخزين القرص التحكم الكامل في تخزين الملفات على القرص.\n\n<div dir='ltr'>\n\n```javascript\nvar storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now())\n  }\n})\n\nvar upload = multer({ storage: storage })\n```\n\n</div>\n\nهناك خياران متاحان ، `destination` و `filename`. كلاهما يعملان على تحديد مكان تخزين الملف.\n\nيتم استخدام `destination` لتحديد أي مجلد يجب تخزين الملفات المحملة. يمكن أيضًا إعطاء هذا كـ`سلسلة` (مثل `'/tmp/uploads'`). إذا لم يتم إعطاء `destination` ، فسيتم استخدام الدليل الافتراضي لنظام التشغيل للملفات المؤقتة.\n\n**ملاحظة:** أنت مسؤول عن إنشاء الدليل عند توفر `destination` كدالة. عند المرور بسلسلة ، سوف يتأكد multer من إنشاء الدليل من أجلك.\n\nيتم استخدام `اسم الملف` لتحديد ما يجب تسمية الملف داخل المجلد. إذا لم يتم تقديم `اسم الملف`، فسيتم إعطاء كل ملف اسمًا عشوائيًا لا يتضمن أي امتداد للملف.\n\n**ملاحظة:** لن يقوم multer بإلحاق اي ملحق ملف لك، الدالة الخاص بك يجب أن تقوم بإرجاع اسم ملف كامل بملحق الملف.\n\nيتم تمرير كل دالة من خلال الطلب (req`) وبعض المعلومات حول الملف (`file`) للمساعدة في اتخاذ القرار.\n\nلاحظ أن `req.body` ربما لم يتم ملؤها بالكامل بعد. يعتمد ذلك على الترتيب الذي يقوم به العميل من خلال نقل المدخلات والملفات إلى الخادم.\n\n#### `MemoryStorage`\n\nيخزن محرك تخزين الذاكرة الملفات الموجودة في الذاكرة ككائنات `ذاكرة` (`Buffer`). ليس لديها أي خيارات.\n\n<div dir='ltr'>\n\n```javascript\nvar storage = multer.memoryStorage()\nvar upload = multer({ storage: storage })\n```\n\n</div>\n\nعند استخدام ذاكرة التخزين ، ستحتوي معلومات الملف على مُدخَل يسمى `buffer` الذي يحتوي على الملف بأكمله.\n\n**تحذير**: يمكن أن يؤدي تحميل ملفات كبيرة جدًا أو ملفات صغيرة نسبيًا بأعداد كبيرة و بسرعة كبيرة إلى نفاد ذاكرة التطبيق عند استخدام ذاكرة التخزين.\n\n### `limits`\n\nكائن يحدد حدود حجم الخصائص الاختيارية التالية. يقوم Multer بتمرير هذا الكائن إلى busboy مباشرة ، ويمكن العثور على تفاصيل الخصائص من خلال [صفحة busboy's](https://github.com/mscdex/busboy#busboy-methods).\n\nتتوفر القيم الصحيحة التالية:\n\nمفتاح | وصف | افتراضي\n--- | --- | ---\n`fieldNameSize` | الحد الأقصى لحجم اسم المُدخَل | 100 بايت\n`fieldSize` | الحد الأقصى لحجم قيمة المُدخَل (بالبايت) | 1 ميغابايت\n`fields` | الحد الأقصى لعدد المدخلات التى لا تعتبر من الملفات | ما لا نهاية\n`fileSize` | حجم الملف الأقصى بالنسبة لإستمارة متعددة الأجزاء (بالبايت) | ما لا نهاية\n`files` | الحد الأقصى لعدد المدخلات من نوع الملفات بالنسبة لإستمارة متعددة الأجزاء | ما لا نهاية\n`parts` | الحد الأقصى لعدد الأجزاء (مدخلات + ملفات) بالنسبة لإستمارة متعددة الأجزاء | ما لا نهاية\n`headerPairs` | الحد الأقصى لعدد أزواج الرأس (المفتاح => القيمة) المطلوب تحليلها بالنسبة لإستمارة متعددة الأجزاء | 2000\n\nيمكن أن يساعد تحديد الحدود في حماية موقعك من هجمات حجب الخدمة (DoS).\n\n### `fileFilter`\n\nاضبط هذا على دالة للتحكم في الملفات التي ينبغي تحميلها وأي الملفات يجب تخطيها. يجب أن تبدو دالة كما يلي:\n\n<div dir='ltr'>\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // The function should call `cb` with a boolean\n  // to indicate if the file should be accepted\n\n  // To reject this file pass `false`, like so:\n  cb(null, false)\n\n  // To accept the file pass `true`, like so:\n  cb(null, true)\n\n  // You can always pass an error if something goes wrong:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n</div>\n\n## معالجة الأخطاء\n\nعند مواجهة خطأ ، سيقوم Multer بتفويض الخطأ إلى Express. يمكنك\nعرض صفحة خطأ لطيفة باستخدام [طريقة Express القياسية](http://expressjs.com/guide/error-handling.html).\n\nإذا كنت تريد إنتقاء الأخطاء والحصول على [أخطاء Multer فقط](https://github.com/expressjs/multer/blob/main/lib/multer-error.js)، فيمكنك نداء بدالة الوسيطة من قبل نفسك. أيضًا ، إذا كنت تريد التقاط أخطاء Multer فقط ، فيمكنك استخدام صنف `MulterError` المتصل بالكائن` multer` نفسه (على سبيل المثال `err instanceof multer.MulterError`).\n\n<div dir='ltr'>\n\n```javascript\nvar multer = require('multer')\nvar upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // A Multer error occurred when uploading.\n    } else if (err) {\n      // An unknown error occurred when uploading.\n    }\n\n    // Everything went fine.\n  })\n})\n```\n\n</div>\n\n## محرك التخزين الخاص بك\n\nللحصول على معلومات حول كيفية إنشاء محرك التخزين الخاص بك ، راجع [محرك تخزين Multer](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## الترخيص\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-es.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter es un \"*middleware*\" de node.js para el manejo de `multipart/form-data`, el cuál es usado sobre todo para la subida de archivos. Está escrito sobre [busboy](https://github.com/mscdex/busboy) para maximizar su eficiencia.\n\n**NOTA**: Multer no procesará ningún formulario que no sea multiparte (`multipart/form-data`).\n\n## Traducciones\n\nÉste archivo README también está disponible en otros lenguajes:\n\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (Inglés)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (Chino)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (Coreano)\n- [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (Ruso)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (Portugués Brasileño)\n\n## Instalación\n\n```sh\n$ npm install --save multer\n```\n\n## Uso\n\nMulter añade un objeto `body` y un objeto `file` o `files` al objeto `request`. El objeto `body` contiene los valores correspondientes a los campos de texto del formulario, los objetos `file` o `files` contienen los archivos que serán subidos mediante el formulario.\n\nEjemplo básico de cómo usarlo:\n\nNo te olvides de `enctype=\"multipart/form-data\"` en tu formulario.\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nconst express = require('express')\nconst multer  = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file es el archivo del `avatar`\n  // req.body contendrá los campos de texto, si los hubiera.\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files es el arreglo (array) de archivos `photos`\n  // req.body contendrá los campos de texto, si los hubiera.\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files es un objeto (String -> Array) donde el nombre del campo es la clave (key) y el valor es el arreglo (array) de archivos\n  //\n  // Ejemplo\n  //  req.files['avatar'][0] -> Archivo\n  //  req.files['gallery'] -> Array\n  //\n  // req.body contendrá los campos de texto, si los hubiera.\n})\n```\n\nEn caso de que necesites manejar un formulario multiparte (multipart form) que sólo contiene campos de texto, deberias usar el método `.none()`:\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body contiene los campos textuales\n})\n```\n\n\nEste es un ejemplo de cómo se utiliza multer en un formulario HTML. Presta especial atención en los campos `enctype=\"multipart/form-data\"` y `name=\"uploaded_file\"`:\n\n```html\n<form action=\"/stats\" enctype=\"multipart/form-data\" method=\"post\">\n  <div class=\"form-group\">\n    <input type=\"file\" class=\"form-control-file\" name=\"uploaded_file\">\n    <input type=\"text\" class=\"form-control\" placeholder=\"Number of speakers\" name=\"nspeakers\">\n    <input type=\"submit\" value=\"Get me the stats!\" class=\"btn btn-default\">\n  </div>\n</form>\n```\n\nLuego en tu archivo javascript agrega estas líneas para acceder tanto al archivo (file) como al body.Es importante que uses el valor del campo `name` del formulario, en tu función de subida. Esto le indica a multer en qué campo de la petición debe buscar los archivos. Si estos campos no son los mismos en el formulario HTML y en tu servidor, la subida fallará:\n\n```javascript\nconst multer  = require('multer')\nconst upload = multer({ dest: './public/data/uploads/' })\napp.post('/stats', upload.single('uploaded_file'), function (req, res) {\n   // req.file es el nombre de tu archivo en el formulario anterior, en este caso 'uploaded_file'\n   // req.body contendrá los campos de texto, si los hubiera.\n   console.log(req.file, req.body)\n});\n```\n\n\n\n## API\n\n### Información del archivo\n\nCada archivo contiene la siguiente información:\n\nClave (Key) | Descripción | Nota\n--- | --- | ---\n`fieldname` | Nombre del campo especificado en el formulario |\n`originalname` | Nombre del archivo en la computadora del usuario |\n`encoding` | Tipo de codificación del archivo |\n`mimetype` | Mime type del archivo |\n`size` | Tamaño del archivo en Bytes |\n`destination` | La carpeta donde se guardó el archivo | `DiskStorage`\n`filename` | El nombre del archivo en `destination` | `DiskStorage`\n`path` | La ruta completa al archivo subido | `DiskStorage`\n`buffer` | Un `Buffer` del archivo completo | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter acepta un objeto para configurar sus opciones, la más básica de ellas es la propiedad `dest`, la cual informa a Multer dónde debe subir los archivos. En caso de que omitas el objeto con las opciones, los archivos serán guardados en la memoria y nunca serán escritos en el disco.\n\nPor defecto, Multer renombrará los archivos para evitar conflictos de nombres. La función usada para renombrarlos puede ser modificada acorde a tus necesidades.\n\nLas siguientes son las opciones que pueden ser utilizadas con Multer.\n\nClave (key) | Descripción\n--- | ---\n`dest` o `storage` | Donde se guardarán los archivos\n`fileFilter` | Función para controlar qué archivos son aceptados\n`limits` | Límites de los datos subidos\n`preservePath` | Mantiene la ruta completa de la ubicación de los archivos, en vez de sólo sus nombres\n`defParamCharset` | Conjunto de caracteres por defecto para usar en valores de parámetros de encabezados de partes (ej. nombre de archivo) que no son parámetros extendidos (que contienen un conjunto de caracteres explícito). Por defecto: `'latin1'`\n\nEn la aplicación web promedio es probable que sólo se requiera `dest`, siendo configurado como en el siguiente ejemplo:\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\n\nSi quieres más control sobre tus subidas, tendrás que usar la opción `storage` en vez de `dest`. Multer incorpora los mecanismos de almacenamiento `DiskStorage` y `MemoryStorage`; existen otros medios provistos por terceros.\n\n#### `.single(fieldname)`\n\nAcepta un único archivo con el nombre `fieldname`. Dicho archivo será guardado en `req.file`.\n\n#### `.array(fieldname[, maxCount])`\n\nAcepta un arreglo (array), de archivos, todos con el nombre `fieldname`. Opcionalmente puede generarse un error si se intentan subir una cantidad de archivos superior a `maxCount`. El arreglo (array) de archivos será guardado en `req.files`.\n\n#### `.fields(fields)`\n\nAcepta una mezcla de archivos, especificados por `field`. Un objeto con arreglos (arrays) de archivos será guardado en `req.files`\n\n`fields` debería ser un arreglo (array) de objetos con `name` y opcionalmente `maxCount`.\nEjemplo:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\nAcepta sólo campos de texto. En caso de intentar subir un archivo, se generará un error con el siguiente código\n\"LIMIT\\_UNEXPECTED\\_FILE\".\n\n#### `.any()`\n\nAcepta todos los archivos que han sido enviados. Un arreglo (array) conteniendo los archivos, será guardado en `req.files`.\n\n**ADVERTENCIA:** Asegúrate de siempre manejar los archivos que los usuarios intenten subir. Nunca uses Multer como una función middleware de manera global dado que, de esta forma, un usuario malicioso podría subir archivos por medio de rutas que no has anticipado. Usa sólo esta función en rutas en las que estás esperando archivos.\n\n### `storage`\n\n#### `DiskStorage`\n\nEl motor de almacenamiento en disco te ofrece un control total sobre el almacenamiento de archivos en tu disco.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now())\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\nHay dos opciones disponibles, `destination` y `filename`. Ambas son funciones que determinan dónde debería almacenarse el archivo.\n\n`destination` se utiliza para determinar en qué carpeta se almacenarán los archivos subidos. Tambien se puede proporcionar como un `string` (por ejemplo: `'/tmp/uploads'`). Si no se proporciona `destination`, se utilizara el directorio predeterminado del sistema operativo para archivos temporales.\n\n**Nota:** Al pasar `destination` como una función, tú eres el responsable de crear los directorios donde los archivos serán almacenados. Cuando asignas un `string` a `destination`, Multer se asegurará de que el directorio sea creado en caso de no encontrarlo.\n\n`filename` es usado para determinar cómo debería ser nombrado el archivo dentro de la carpeta. Si `filename` no es provisto, a cada archivo se le asignará un nombre aleatorio que no incluirá ninguna extensión.\n\n**Nota:** Multer no añadirá ningúna extensión de archivos por ti, es tu función la que debería retornar un nombre completo, que incluya también la extensión del archivo.\n\nEl objeto petición (`req`) y parte de la información del archivo (`file`) son pasadas a tu función para ayudar con la decisión en la nomenclatura.\n\nNota que  `req.body` puede que no haya sido totalmente poblado todavía. Esto depende del orden en el que el cliente transmita sus campos y archivos hacia el servidor.\n\nPara comprender la convención de llamada utilizada en el callback (necesitas pasar null como primer parametro), consulta en\n[Node.js manejo de errores](https://web.archive.org/web/20220417042018/https://www.joyent.com/node-js/production/design/errors)\n\n#### `MemoryStorage`\n\nEl motor de almacenamiento en memoria almacena los archivos en memoria como objetos `Buffer`. Para esto no se proveen opciones.\n\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\n\nAl usar el almacenamiento en memoria, la información del archivo contendrá un campo llamado `buffer` que contiene el archivo entero.\n\n**ADVERTENCIA**: Subir archivos grandes, o relativamente pequeños pero en gran cantidad y muy rápido, puede provocar que tu aplicación se quede sin memoria cuando es usado el almacenamiento en memoria.\n\n### `limits`\n\nUn objeto especifica los límites correpondientes a los tamaños de las siguientes propiedades opcionales. Multer pasa este objeto directamente a *busboy*, los detalles de las propiedades pueden encontrarse en [la página de busboy](https://github.com/mscdex/busboy#busboy-methods).\n\nLos siguientes valores en números enteros están disponibles:\n\nClave (Key) | Descripción | Por defecto\n--- | --- | ---\n`fieldNameSize` | Tamaño máximo del nombre del campo | 100 bytes\n`fieldSize` | Tamaño máximo de los valores para cada campo (en bytes) | 1MB\n`fields` | Número máximo de campos que no son archivos | Infinito\n`fileSize` | Para formularios multiparte, el tamaño máximo de los archivos (en bytes) | Infinito\n`files` | Para los formularios multiparte, el número máximo de campos para archivos | Infinito\n`parts` | Para los formularios multiparte, el número máximo de partes (campos + archivos) | Infinito\n`headerPairs` | Para los formularios multiparte, el número máximo de cabeceras de pares clave=>valor para analizar | 2000\n\nEspecificar los límites puede ayudarte a proteger tu sitio contra ataques de denegación del servicio (DoS).\n\n### `fileFilter`\n\nAsigna ésto a una función para controlar cuáles archivos deben ser subidos y cuáles deben ser omitidos. La función debería verse como ésta:\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // La función debe llamar a `cb` usando una variable del tipo boolean\n  // para indicar si el archivo debería ser aceptado o no\n\n  // Para rechazar el archivo es necesario pasar `false`, de la siguiente forma:\n  cb(null, false)\n\n  // Para aceptar el archivo es necesario pasar `true`, de la siguiente forma:\n  cb(null, true)\n\n  // Siempre puedes pasar un error en caso de que algo salga mal:\n  cb(new Error('No tengo la menor idea!'))\n\n}\n```\n\n## Manejo de errores\n\nAl encontrarse con un error, Multer delegará ese error a Express. Puedes mostrar una linda página de error usando [la manera standard de Express](http://expressjs.com/guide/error-handling.html).\n\nSi quieres capturar los errores específicamente desde Multer, puedes llamar la función middleware tú mismo. También, si quieres capturar sólo [los errores de Multer](https://github.com/expressjs/multer/blob/main/lib/multer-error.js), puedes usar la clase `MulterError` que está adherida al mismo objeto `multer` (por ejemplo: `err instanceof multer.MulterError`).\n\n```javascript\nconst multer = require('multer')\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // Un error de Multer ocurrió durante la subida.\n    } else if (err) {\n      // Un error desconocido ocurrió durante la subida.\n    }\n\n    // Todo salió bien.\n  })\n})\n```\n\n## Mecanismos de almacenamiento personalizados\n\nPara más información acerca de cómo construir tu propio mecanismo de almacenamiento, recomendamos leer [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## Licencia\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-fr.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter est un middleware node.js pour la gestion des données `multipart/form-data` qui est principalement utilisé pour télécharger des fichiers.\nIl est écrit au-dessus de [busboy](https://github.com/mscdex/busboy) pour une efficacité maximale.\n\n**NOTE**: Multer ne traitera aucun formulaire qui ne soit pas un multipart (`multipart/form-data`).\n\n## Translations\n\nThis README is also available in other languages:\n\n- [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md) (Arabe)\n- [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) (Espagnol)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (Chinois)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (Coréen)\n- [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (Russe)\n- [Việt Nam](https://github.com/expressjs/multer/blob/main/doc/README-vi.md) (Vietnamien)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (Portugais du Brésil)\n- [Français](https://github.com/expressjs/multer/blob/main/doc/README-fr.md) (Français)\n\n## Installation\n\n```sh\n$ npm install --save multer\n```\n\n## Usage\n\nMulter ajoute un objet `body` et un objet `file` ou `files` à l'objet `request`. L'objet `body` contient les valeurs des champs texte du formulaire, l'objet `file` ou `files` contient les fichiers téléchargés via le formulaire.\n\nExemple d'utilisation de base :\n\nN'oubliez pas le `enctype=\"multipart/form-data\"` dans votre formulaire.\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nconst express = require('express')\nconst multer  = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file est le fichier `avatar`\n  // req.body contiendra les champs de texte, s'il y en avait\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files est un tableau de fichiers \"photos\"\n  // req.body contiendra les champs de texte, s'il y en avait\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files est un objet (String -> Array) où fieldname est la clé et la valeur est un tableau de fichiers\n  //\n  // e.g.\n  //  req.files['avatar'][0] -> Fichier\n  //  req.files['gallery'] -> Tableau\n  //\n  // req.body contiendra les champs de texte, s'il y en avait\n})\n```\n\nDans le cas où vous auriez besoin de gérer un formulaire en plusieurs parties texte uniquement, vous devez utiliser la méthode `.none()`:\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body contiens les champs de text\n})\n```\n\nVoici un exemple d'utilisation de multer dans un formulaire HTML. Faites particulièrement attention aux champs `enctype=\"multipart/form-data\"` et `name=\"uploaded_file\"`:\n\n```html\n<form action=\"/stats\" enctype=\"multipart/form-data\" method=\"post\">\n  <div class=\"form-group\">\n    <input type=\"file\" class=\"form-control-file\" name=\"uploaded_file\">\n    <input type=\"text\" class=\"form-control\" placeholder=\"Number of speakers\" name=\"nspeakers\">\n    <input type=\"submit\" value=\"Get me the stats!\" class=\"btn btn-default\">\n  </div>\n</form>\n```\n\nEnsuite, dans votre fichier javascript, vous ajouterez ces lignes pour accéder à la fois au fichier et au corps. Il est important que vous utilisiez la valeur du champ `name` du formulaire dans votre fonction de téléchargement. Cela indique à Multer dans quel champ de la requête il doit rechercher les fichiers. Si ces champs ne sont pas les mêmes dans le formulaire HTML et sur votre serveur, votre téléchargement échouera:\n```javascript\nconst multer  = require('multer')\nconst upload = multer({ dest: './public/data/uploads/' })\napp.post('/stats', upload.single('uploaded_file'), function (req, res) {\n  // req.file est le nom de votre fichier dans le formulaire ci-dessus, ici 'uploaded_file'\n  // req.body contiendra les champs de texte, s'il y en avait\n  console.log(req.file, req.body)\n});\n```\n## API\n\n### Informations sur les fichiers\n\nChaque fichier contient les informations suivantes:\n\nClé | Description                                    | Notes\n--- |------------------------------------------------| ---\n`fieldname` | Nom du champ spécifié dans le formulaire       |\n`originalname` | Nom du fichier sur l'ordinateur de l'utilisateur |\n`encoding` | Type d'encodage du fichier                     |\n`mimetype` | Type Mime du fichier                           |\n`size` | Taille du fichier en octets                      |\n`destination` | TLe dossier dans lequel le fichier a été enregistré    | `DiskStorage`\n`filename` | Le nom du fichier dans la `destination`    | `DiskStorage`\n`path` | Le chemin d'accès complet au fichier téléchargé             | `DiskStorage`\n`buffer` | Un `Buffer` du fichier entier                  | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter accepte un objet d'options, dont le plus basique est le `dest`\npropriété, qui indique à Multer où télécharger les fichiers. Au cas où vous omettez l'objet\noptions, les fichiers seront conservés en mémoire et ne seront jamais écrits sur le disque.\n\nPar défaut, Multer renommera les fichiers afin d'éviter les conflits de nommage. Les\nla fonction de renommage peut être personnalisée en fonction de vos besoins.\n\nVoici les options qui peuvent être transmises à Multer.\n\nClé | Description\n--- | ---\n`dest` ou `storage` | Où stocker les fichiers\n`fileFilter` | Fonction pour contrôler quels fichiers sont acceptés\n`limits` | Limites des données téléchargées\n`preservePath` | Conservez le chemin complet des fichiers au lieu du nom de base uniquement\n`defParamCharset` | Jeu de caractères par défaut à utiliser pour les valeurs des paramètres d'en-tête de partie (par exemple, nom de fichier) qui ne sont pas des paramètres étendus (qui contiennent un jeu de caractères explicite). Par défaut : `'latin1'`\n\nDans une application Web moyenne, seul `dest` peut être requis et configuré comme indiqué dans\nl'exemple suivant.\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\n\nSi vous voulez plus de contrôle sur vos téléchargements, vous voudrez utiliser le `storage`\noption au lieu de `dest`. Multer est livré avec des moteurs de stockage `DiskStorage`\net `MemoryStorage`; D'autres moteurs sont disponibles auprès de tiers.\n\n#### `.single(fieldname)`\n\nAcceptez un seul fichier avec le nom `fieldname`. Le fichier unique sera stocké\ndans `req.file`.\n\n#### `.array(fieldname[, maxCount])`\n\nAcceptez un tableau de fichiers, tous avec le nom `fieldname`. Eventuellement erreur si\nplus de `maxCount` fichiers sont téléchargés. Le tableau de fichiers sera stocké dans\n`req.files`.\n\n#### `.fields(fields)`\n\nAccepte un mélange de fichiers, spécifié par `fields`. Un objet avec des tableaux de fichiers\nseront stockés dans `req.files`.\n\n`fields` doit être un tableau d'objets avec `name` et éventuellement un `maxCount`.\nExemple:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\nN'acceptez que les champs de texte. Si un téléchargement de fichier est effectué, une erreur avec le code\n\"LIMIT\\_UNEXPECTED\\_FILE\" sera émis.\n\n#### `.any()`\n\nAccepte tous les fichiers qui arrivent sur le fil. Un tableau de fichiers sera stocké dans\n`req.files`.\n\n**ATTENTION:** Assurez-vous de toujours gérer les fichiers qu'un utilisateur télécharge.\nN'ajoutez jamais multer en tant que middleware global car un utilisateur malveillant pourrait télécharger des\nfichiers vers un itinéraire que vous n'aviez pas prévu. N'utilisez cette fonction que sur les itinéraires\noù vous gérez les fichiers téléchargés.\n\n### `storage`\n\n#### `DiskStorage`\n\nLe moteur de stockage sur disque vous donne un contrôle total sur le stockage des fichiers sur le disque.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)\n    cb(null, file.fieldname + '-' + uniqueSuffix)\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\nIl y a deux options disponibles, `destination` et `filename`. Elles sont toutes les deux\ndes fonctions qui déterminent où le fichier doit être stocké.\n\n`destination` est utilisé pour déterminer dans quel dossier les fichiers téléchargés doivent\nêtre stocké. Cela peut également être donné sous forme de `string` (par exemple `'/tmp/uploads'`). Sinon\n`destination` est donné, le répertoire par défaut du système d'exploitation est utilisé pour les\nfichiers temporaires.\n\n**Remarque:** Vous êtes responsable de la création du répertoire lorsque vous fournissez\n`destination` en tant que fonction. Lors du passage d'une chaîne, multer s'assurera que\nle répertoire est créé pour vous.\n\n`filename` est utilisé pour déterminer le nom du fichier dans le dossier.\nSi aucun \"nom de fichier\" n'est donné, chaque fichier recevra un nom aléatoire qui n'inclut\npas d'extension de fichier.\n\n**Remarque:** Multer n'ajoutera aucune extension de fichier pour vous, votre fonction\ndoit renvoyer un nom de fichier complet avec une extension de fichier.\n\nChaque fonction reçoit à la fois la requête (`req`) et des informations sur\nle dossier (`file`) pour aider à la décision.\n\nNotez que `req.body` n'a peut-être pas encore été entièrement rempli. Cela dépend de l'ordre\noù le client transmet les champs et les fichiers au serveur.\n\nPour comprendre la convention d'appel utilisée dans le rappel (nécessité de passer\nnull comme premier paramètre), reportez-vous à\n[Node.js error handling](https://web.archive.org/web/20220417042018/https://www.joyent.com/node-js/production/design/errors)\n\n#### `MemoryStorage`\n\nLe moteur de stockage en mémoire stocke les fichiers en mémoire en tant qu'objets `Buffer`. Il\nn'a pas d'options.\n\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\n\nLors de l'utilisation du stockage en mémoire, les informations sur le fichier contiendront un champ appelé\n`buffer` qui contient le fichier entier.\n\n**ATTENTION**: Le téléchargement de fichiers très volumineux ou de fichiers relativement petits en grand\nnombres très rapidement, peut entraîner un manque de mémoire de votre application lorsque\nle stockage en mémoire est utilisé.\n\n### `limits`\n\nUn objet spécifiant les limites de taille des propriétés facultatives suivantes. Multer passe directement cet objet dans busboy, et les détails des propriétés peuvent être trouvés sur [la page de busboy](https://github.com/mscdex/busboy#busboy-methods).\n\nLes valeurs entières suivantes sont disponibles :\n\nClé | Description                                                               | Default\n--- |---------------------------------------------------------------------------| ---\n`fieldNameSize` | Taille maximale du nom de champ                                           | 100 bytes\n`fieldSize` | Max field value size (in bytes)                                           | 1MB\n`fields` | Taille maximale de la valeur du champ (en octets)                         | Infinity\n`fileSize` | Pour les formulaires multipart, la taille maximale du fichier (en octets) | Infinity\n`files` | Pour les formulaires multipart, le nombre maximal de champs de fichier    | Infinity\n`parts` | Pour les formulaires multipart, le nombre max de parties (champs + fichiers)         | Infinity\n`headerPairs` | Pour les formulaires multipart, le nombre maximum de paires clé=>valeur d'en-tête à analyser   | 2000\n\nSpécifier les limites peut aider à protéger votre site contre les attaques par déni de service (DoS).\n\n### `fileFilter`\n\nDéfinissez ceci sur une fonction pour contrôler quels fichiers doivent être téléchargés et lesquels\ndevrait être ignoré. La fonction devrait ressembler à ceci:\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // La fonction doit appeler `cb` avec un booléen\n  // pour indiquer si le fichier doit être accepté\n\n  // Pour rejeter ce fichier, passez `false`, comme ceci:\n  cb(null, false)\n\n  // Pour accepter le fichier, passez `true`, comme ceci:\n  cb(null, true)\n\n  // Vous pouvez toujours passer une erreur si quelque chose ne va pas:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n## Gestion des Erreurs\n\nEn cas d'erreur, Multer déléguera l'erreur à Express. Vous pouvez\nafficher une belle page d'erreur en utilisant [la voie express standard](http://expressjs.com/guide/error-handling.html).\n\nSi vous souhaitez détecter les erreurs spécifiquement de Multer, vous pouvez appeler la\nfonction middleware par vous-même. Aussi, si vous voulez attraper seulement [les erreurs Multer](https://github.com/expressjs/multer/blob/main/lib/multer-error.js), vous pouvez utiliser la classe `MulterError` qui est jointe à l'objet `multer` lui-même (par exemple `err instanceof multer.MulterError`).\n\n```javascript\nconst multer = require('multer')\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // Une erreur Multer s'est produite lors du téléchargement.\n    } else if (err) {\n      // Une erreur inconnue s'est produite lors du téléchargement.\n    }\n\n    // Tout s'est bien passé.\n  })\n})\n```\n\n## Moteur de stockage personnalisé\n\nPour plus d'informations sur la création de votre propre moteur de stockage, consultez [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## License\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-ko.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter는 파일 업로드를 위해 사용되는 `multipart/form-data` 를 다루기 위한 node.js 의 미들웨어 입니다. 효율성을 최대화 하기 위해 [busboy](https://github.com/mscdex/busboy) 를 기반으로 하고 있습니다.\n\n**주**: Multer는 multipart (`multipart/form-data`)가 아닌 폼에서는 동작하지 않습니다.\n\n## 번역\n\n이 문서는 아래의 언어로도 제공됩니다:\n\n- [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md) (아라비아 말)\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (영어)\n- [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) (스페인어)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (중국어)\n- [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (러시아)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (포르투갈어 BR)\n\n## 설치\n\n```sh\n$ npm install --save multer\n```\n\n## 사용법\n\nMulter는 `body` 객체와 한 개의 `file` 혹은 여러개의 `files` 객체를 `request` 객체에 추가합니다. `body` 객체는 폼 텍스트 필드의 값을 포함하고, 한 개 혹은 여러개의 파일 객체는 폼을 통해 업로드된 파일들을 포함하고 있습니다.\n\n기본 사용 예제:\n\n```javascript\nconst express = require('express')\nconst multer  = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file 은 `avatar` 라는 필드의 파일 정보입니다.\n  // 텍스트 필드가 있는 경우, req.body가 이를 포함할 것입니다.\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files 는 `photos` 라는 파일정보를 배열로 가지고 있습니다.\n  // 텍스트 필드가 있는 경우, req.body가 이를 포함할 것입니다.\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files는 (String -> Array) 형태의 객체 입니다.\n  // 필드명은 객체의 key에, 파일 정보는 배열로 value에 저장됩니다.\n  //\n  // e.g.\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // 텍스트 필드가 있는 경우, req.body가 이를 포함할 것입니다.\n})\n```\n\n텍스트 전용 multipart 폼을 처리해야 하는 경우, 어떠한 multer 메소드 (`.single()`, `.array()`, `fields()`) 도 사용할 수 있습니다. 아래는 `.array()` 를 사용한 예제 입니다 :\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.array(), function (req, res, next) {\n  // req.body는 텍스트 필드를 포함합니다.\n})\n```\n\n## API\n\n### 파일 정보\n\n각각의 파일은 아래의 정보를 포함하고 있습니다:\n\nKey | Description | Note\n--- | --- | ---\n`fieldname` | 폼에 정의된 필드 명 |\n`originalname` | 사용자가 업로드한 파일 명 |\n`encoding` | 파일의 엔코딩 타입 |\n`mimetype` | 파일의 Mime 타입 |\n`size` | 파일의 바이트(byte) 사이즈 |\n`destination` | 파일이 저장된 폴더 | `DiskStorage`\n`filename` | `destination` 에 저장된 파일 명 | `DiskStorage`\n`path` | 업로드된 파일의 전체 경로 | `DiskStorage`\n`buffer` | 전체 파일의 `Buffer` | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter는 옵션 객체를 허용합니다. 그 중 가장 기본 옵션인 `dest` 요소는 Multer에게 파일을 어디로 업로드 할 지를 알려줍니다. 만일 옵션 객체를 생략했다면, 파일은 디스크가 아니라 메모리에 저장될 것 입니다.\n\n기본적으로 Multer는 이름이 중복되는 것을 방지하기 위해서 파일의 이름을 재작성 합니다. 필요에 따라 해당 함수는 커스터마이징이 가능합니다.\n\nMulter로 전달 가능한 옵션들은 다음과 같습니다.\n\nKey | Description\n--- | ---\n`dest` or `storage` | 파일이 저장될 위치\n`fileFilter` | 어떤 파일을 허용할지 제어하는 함수\n`limits` | 업로드 된 데이터의 한도\n`preservePath` | 파일의 base name 대신 보존할 파일의 전체 경로\n`defParamCharset` | 확장 매개변수가 아닌 부분 헤더 매개변수 값(예: 파일명)에 사용할 기본 문자 집합(명시적 문자 집합을 포함하지 않음). 기본값: `'latin1'`\n\n보통의 웹 앱에서는 `dest` 옵션 정도만 필요할지도 모릅니다. 설정 방법은 아래의 예제에 나와있습니다.\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\n\n만일 업로드를 더 제어하고 싶다면, `dest` 옵션 대신 `storage` 옵션을 사용할 수 있습니다. Multer는 스토리지 엔진인 `DiskStorage` 와 `MemoryStorage` 를 탑재하고 있습니다. 써드파티로부터 더 많은 엔진들을 사용할 수 있습니다.\n\n#### `.single(fieldname)`\n\n`fieldname` 인자에 명시된 이름의 단수 파일을 전달 받습니다. 이 파일은 `req.file` 에 저장될 것 입니다.\n\n#### `.array(fieldname[, maxCount])`\n\n`fieldname` 인자에 명시된 이름의 파일 전부를 배열 형태로 전달 받습니다. 선택적으로 `maxCount` 에 명시된 값 이상의 파일이 업로드 될 경우 에러를 출력할 수 있습니다. 전달 된 배열 형태의 파일은 `req.files` 에 저장될 것입니다.\n\n#### `.fields(fields)`\n\n`fields` 인자에 명시된 여러 파일을 전달 받습니다. 파일 객체는 배열 형태로 `req.files` 에 저장될 것입니다.\n\n`fields` 는 `name` 과 `maxCount` (선택사항) 을 포함하는 객체의 배열이어야 합니다.\n예제:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\n오직 텍스트 필드만 허용합니다. 만일 파일이 업로드 되었을 경우, \"LIMIT\\_UNEXPECTED\\_FILE\" 와 같은 에러 코드가 발생할 것입니다. 이는 `upload.fields([])` 와 같은 동작을 합니다.\n\n#### `.any()`\n\n전달된 모든 파일을 허용합니다. 파일 배열은 `req.files` 에 저장될 것입니다.\n\n**주의:** 항상 사용자가 업로드한 파일을 다룬다는 점을 명심하세요. 악의적인 사용자가 여러분이 예측하지 못한 곳으로 파일을 업로드 할 수 있으므로 절대 multer를 글로벌 미들웨어로 사용하지 마세요.\n\n### `storage`\n\n#### `DiskStorage`\n\n디스크 스토리지 엔진은 파일을 디스크에 저장하기 위한 모든 제어 기능을 제공합니다.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now())\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\n`destination` 과 `filename` 의 두가지 옵션이 가능합니다. 두 옵션 모두 파일을 어디에 저장할 지를 정하는 함수입니다.\n\n`destination` 옵션은 어느 폴더안에 업로드 한 파일을 저장할 지를 결정합니다. 이는 `string` 형태로 주어질 수 있습니다 (예. `'/tmp/uploads'`). 만일 `destination` 옵션이 주어지지 않으면, 운영체제 시스템에서 임시 파일을 저장하는 기본 디렉토리를 사용합니다.\n\n**주:** `destination` 을 함수로 사용할 경우, 디렉토리를 생성해야 할 책임이 있습니다. 문자열이 전달될 때, multer는 해당 디렉토리가 생성되었는지 확인합니다.\n\n`filename` 은 폴더안에 저장되는 파일 명을 결정하는데 사용됩니다.\n만일 `filename` 이 주어지지 않는다면, 각각의 파일은 파일 확장자를 제외한 랜덤한 이름으로 지어질 것입니다.\n\n**주:** Multer는 어떠한 파일 확장자도 추가하지 않습니다. 사용자 함수는 파일 확장자를 온전히 포함한 파일명을 반환해야 합니다.\n\n결정을 돕기 위해 각각의 함수는 요청 정보 (`req`) 와 파일 (`file`) 에 대한 정보를 모두 전달 받습니다.\n\n`req.body` 는 완전히 채워지지 않았을 수도 있습니다. 이는 클라이언트가 필드와 파일을 서버로 전송하는 순서에 따라 다릅니다.\n\n#### `MemoryStorage`\n\n메모리 스토리지 엔진은 파일을 메모리에 `Buffer` 객체로 저장합니다. 이에 대해서는 어떤 옵션도 없습니다.\n\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\n\n메모리 스토리지 사용시, 파일 정보는 파일 전체를 포함하는 `buffer` 라고 불리는 필드를 포함할 것입니다.\n\n**주의**: 메모리 스토리지를 사용시, 매우 큰 사이즈의 파일을 업로드 하거나 많은 양의 비교적 작은 파일들을 매우 빠르게 업로드 하는 경우 응용 프로그램의 메모리 부족이 발생 할 수 있습니다.\n\n### `limits`\n\n다음의 선택적 속성의 크기 제한을 지정하는 객체입니다. Multer 는 이 객체를 busboy로 직접 전달합니다. 속성들에 대한 자세한 내용은 [busboy's page](https://github.com/mscdex/busboy#busboy-methods) 에서 확인 하실 수 있습니다.\n\n다음과 같은 정수 값들이 가능합니다:\n\n속성 | 설명 | 기본값\n--- | --- | ---\n`fieldNameSize` | 필드명 사이즈 최대값 | 100 bytes\n`fieldSize` | 필드값 사이즈 최대값 | 1MB\n`fields` | 파일형식이 아닌 필드의 최대 개수 | 무제한\n`fileSize` | multipart 형식 폼에서 최대 파일 사이즈(bytes) | 무제한\n`files` | multipart 형식 폼에서 파일 필드의 최대 개수 | 무제한\n`parts` | For multipart forms, the max number of parts (fields + files) | 무제한\n`headerPairs` | multipart 형식 폼에서 파싱할 헤더의 key=>value 쌍의 최대 개수| 2000\n\n사이즈 제한을 지정하면 서비스 거부 (DoS) 공격으로부터 사이트를 보호하는데 도움이 됩니다.\n\n### `fileFilter`\n\n어느 파일을 업로드 할지, 혹은 건너뛸지 제어할 수 있게 함수에 설정합니다. 해당 함수는 아래와 같을 것입니다 :\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // 이 함수는 boolean 값과 함께 `cb`를 호출함으로써 해당 파일을 업로드 할지 여부를 나타낼 수 있습니다.\n  // 이 파일을 거부하려면 다음과 같이 `false` 를 전달합니다:\n  cb(null, false)\n\n  // 이 파일을 허용하려면 다음과 같이 `true` 를 전달합니다:\n  cb(null, true)\n\n  // 무언가 문제가 생겼다면 언제나 에러를 전달할 수 있습니다:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n## 에러 핸들링\n\n에러가 발생할 때, multer는 에러를 express에 위임할 것입니다. 여러분은 [the standard express way](http://expressjs.com/guide/error-handling.html) 를 이용해서 멋진 오류 페이지를 보여줄 수 있습니다.\n\n만일 multer 로부터 특별히 에러를 캐치하고 싶다면, 직접 미들웨어 함수를 호출하세요.\n\n```javascript\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err) {\n      // 업로드할때 오류가 발생함\n      return\n    }\n\n    // 정상적으로 완료됨\n  })\n})\n```\n\n## 커스텀 스토리지 엔진\n\n자신만의 고유한 스토리지 엔진을 구축하기 위한 정보를 얻기 위해서는 [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md) 문서를 참고하세요.\n\n## 라이센스\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-pt-br.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter é um middleware node.js para manipulação `multipart/form-data`, que é usado principalmente para fazer upload de arquivos. Foi escrito em cima do [busboy](https://github.com/mscdex/busboy) para máxima eficiência.\n\n**NOTA**: Multer não processará nenhum formulário que não seja multipart (`multipart/form-data`).\n\n## Traduções\n\nEste README também está disponível em outros idiomas:\n\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (Inglês)\n- [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md) (Árabe)\n- [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) (Espanhol)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (Chinês)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (Coreano)\n- [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (Russo)\n- [Việt Nam](https://github.com/expressjs/multer/blob/main/doc/README-vi.md) (Vietnã)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (Português Brasil)\n- [Français](https://github.com/expressjs/multer/blob/main/doc/README-fr.md) (Francês)\n- [O'zbek tili](https://github.com/expressjs/multer/blob/main/doc/README-uz.md) (Uzbequistão)\n\n## Instalação\n\n```sh\n$ npm install --save multer\n```\n\n## Uso\n\nMulter adiciona um objeto `body` e um `file` ou objeto `files` para objeto `request`. O objeto `body` contém os valores dos campos de texto do formulário, o objeto `file` ou `files` contém os arquivos enviados por meio do formulário.\n\nExemplo de uso básico:\n\nNão esqueça o `enctype=\"multipart/form-data\"` em seu formulário.\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nconst express = require('express')\nconst multer = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file é um arquivo `avatar`\n  // req.body conterá os campos de texto, se houver\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files é um array de arquivos `photos`\n  // req.body conterá os campos de texto, se houver\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files é um objeto (String -> Array) onde fieldname é a chave e o valor é array de arquivos\n  //\n  // e.g.\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // req.body conterá os campos de texto, se houver\n})\n```\n\nCaso você precise lidar com formulário text-only multipart, você deve usar o método `.none()`:\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body contém os campos de texto\n})\n```\n\nAqui está um exemplo de como o multer é usado em um formulário HTML. Onde adicionamos `enctype=\"multipart/form-data\"` no form e no input `name=\"uploaded_file\"`:\n\n```html\n<form action=\"/stats\" enctype=\"multipart/form-data\" method=\"post\">\n  <div class=\"form-group\">\n    <input type=\"file\" class=\"form-control-file\" name=\"uploaded_file\">\n    <input type=\"text\" class=\"form-control\" placeholder=\"Número de palestrantes\" name=\"nspeakers\">\n    <input type=\"submit\" value=\"Obter as estatísticas!\" class=\"btn btn-default\">\n  </div>\n</form>\n```\n\nEntão, em seu arquivo javascript, você adicionaria essas linhas para acessar o arquivo e o corpo. É importante que você use o valor do campo `name` do formulário em sua função de upload. Isso informa ao multer em qual campo da solicitação ele deve procurar os arquivos. Se esses campos não forem iguais no formulário HTML e no seu servidor, seu upload falhará:\n\n```javascript\nconst multer  = require('multer')\nconst upload = multer({ dest: './public/data/uploads/' })\napp.post('/stats', upload.single('uploaded_file'), function (req, res) {\n  // req.fileé o nome do seu arquivo no formato acima, aqui 'uploaded_file'\n  // req.body irá conter os campos de texto, se houver algum\n  console.log(req.file, req.body)\n});\n```\n\n## API\n\n### Informação de arquivo\n\nCada arquivo contém as seguintes informações:\n\nKey | Descrição | Nota\n--- | --- | ---\n`fieldname` | Nome do campo especificado no formulário |\n`originalname` | Nome do arquivo no computador do usuário |\n`encoding` | Tipo de codificação do arquivo |\n`mimetype` | Tipo Mime do arquivo |\n`size` | Tamanho do arquivo em bytes |\n`destination` | A pasta na qual o arquivo foi salvo | `DiskStorage`\n`filename` | O nome do arquivo dentro do `destination` | `DiskStorage`\n`path` | O caminho completo para o arquivo enviado | `DiskStorage`\n`buffer` | O `Buffer` do arquivo inteiro | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter aceita um objeto de opções, a propriedade mais básica é o `dest`, que diz ao Multer onde fazer o upload dos arquivos. No caso de você omitir o objeto de opções, os arquivos serão mantidos na memória e nunca gravados no disco.\n\nPor padrão, Multer irá renomear os arquivos para evitar conflitos de nomes. A função de renomeação pode ser personalizada de acordo com suas necessidades.\n\nA seguir estão as opções que podem ser passadas para o Multer.\n\nKey | Descrição\n--- | ---\n`dest` ou `storage` | Onde armazenar os arquivos\n`fileFilter` | Função para controlar quais arquivos são aceitos\n`limits` | Limites dos dados enviados\n`preservePath` | Mantenha o caminho completo dos arquivos em vez de apenas o nome base\n`defParamCharset` | Conjunto de caracteres padrão para usar em valores de parâmetros de cabeçalho de parte (por exemplo, nome do arquivo) que não são parâmetros estendidos (que contêm um conjunto de caracteres explícito). Padrão: `'latin1'`\n\nEm um web app básico, somente o `dest` pode ser necessário, e configurado como mostrado no exemplo a seguir:\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\n\nSe você quiser mais controle sobre seus envios, você ter que usar a opção `storage` em vez de `dest`. Multer vem com motores de armazenamento `DiskStorage` e `MemoryStorage`; Mais mecanismos estão disponíveis de terceiros.\n\n#### `.single(fieldname)`\n\nAceite um único arquivo com o nome `fieldname`. O arquivo único será armazenado em `req.file`.\n\n#### `.array(fieldname[, maxCount])`\n\nAceite múltiplos arquivos, todos com o nome `fieldname`. Opcional, gera um erro se forem enviados mais de `maxCount`. O array de arquivos serão armazenados em\n`req.files`.\n\n#### `.fields(fields)`\n\nAceita uma mistura de arquivos, especificada por `fields`. Um objeto com um array de arquivos será armazenado em `req.files`.\n\n`fields` deve ser uma matriz de objetos com `name` e opcionalmente com `maxCount`.\n\nExemplo:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\nAceite apenas campo de texto. Se algum upload de arquivo for feito, um erro com código \"LIMIT\\_UNEXPECTED\\_FILE\" será emitido.\n\n#### `.any()`\n\nAceita todos os arquivos que são enviados. Uma matriz de arquivos será armazenada em\n`req.files`.\n\n**AVISO:** Certifique-se de sempre manipular os arquivos que um usuário envia.\nNunca adicione o Multer como global no middleware, já que um usuário mal-intencionado poderia fazer upload de arquivos para uma rota que você não previu. Use esta função apenas nas rotas onde você está lidando com os arquivos enviados.\n\n### `storage`\n\n#### `DiskStorage`\n\nO mecanismo de armazenamento em disco oferece controle total sobre o armazenamento de arquivos em disco.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now())\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\nExistem duas opções disponíveis, `destination` e `filename`. Ambas são funções que determinam onde o arquivo deve ser armazenado.\n\n`destination` é usado para determinar em qual pasta os arquivos enviados devem ser armazenados. Isso também pode ser dado como uma `string` (e.g. `'/tmp/uploads'`). Se não é dada `destination`, o diretório padrão do sistema operacional para arquivos temporários é usado.\n\n**Nota:** Você é responsável por criar o diretório ao fornecer o \"destino\" com uma função. Ao passar uma string, o Multer se certificará de que o diretório foi criado para você.\n\n`filename` é usado para determinar qual arquivo deve ser nomeado dentro da pasta.\nSe não for passado `filename`, cada arquivo receberá um nome aleatório que não inclui nenhuma extensão de arquivo.\n\n**Nota:** Multer não adicionará nenhuma extensão de arquivo para você, sua função é retornar um nome para o arquivo completo com a extensão de arquivo.\n\nCada função é passada pelo request (`req`) e algumas informações sobre o arquivo (`file`) para ajudar com a decisão.\n\nObserve que `req.body` pode não ter sido totalmente preenchido ainda. Isso depende da ordem na qual o cliente transmite campos e arquivos para o servidor.\n\nPara entender a convenção de chamada usada no callback (precisando passar\nnull como o primeiro parâmetro), consulte em\n[Manipulação de erros no Node.js](https://web.archive.org/web/20220417042018/https://www.joyent.com/node-js/production/design/errors)\n\n#### `MemoryStorage`\n\nO mecanismo de armazenamento na memória, armazena os arquivos na memória como um objeto `Buffer`. Não tendo opções.\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\nAo usar o armazenamento de memória, as informações do arquivo conterão um campo chamado `buffer` que contém o arquivo inteiro.\n\n**AVISO**: Fazer upload de arquivos muito grandes ou arquivos relativamente pequenos em grande número muito rapidamente pode fazer com que o aplicativo fique sem memória quando o armazenamento de memória é usado.\n\n### `limits`\n\nUm objeto que especifica os limites de tamanho das seguintes propriedades opcionais. O Multer passa diretamente o objeto para o busboy, e os detalhes das propriedades podem ser encontrados em [busboy's page](https://github.com/mscdex/busboy#busboy-methods).\n\nOs seguintes valores inteiros estão disponíveis:\n\nKey | Descrição | Padrão\n--- | --- | ---\n`fieldNameSize` | Tamanho máximo do nome de campo| 100 bytes\n`fieldSize` | Tamanho máximo do valor do campo (in bytes) | 1MB\n`fields` | Max number of non-file fields | Infinity\n`fileSize` | Para formulários multipart, o tamanho máximo do arquivo (in bytes) | Infinity\n`files` | Para formulários multipart, o número máximo de campos de arquivos | Infinity\n`parts` | Para formulários multipart, o número máximo de parts (fields + files) | Infinity\n`headerPairs` | Para formulários multipart, o número máximo do header key=>value, para analisar | 2000\n\nA especificação dos limites pode ajudar a proteger seu site contra ataques de negação de serviço (DoS).\n\n### `fileFilter`\n\nDefina isso para uma função para controlar quais arquivos devem ser enviados e quais devem ser ignorados.\n\nA função deve ficar assim:\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // A função deve chamar `cb` com um booleano\n  // para indicar se o arquivo deve ser aceito\n\n  // Para rejeitar este arquivo passe `false`, assim:\n  cb(null, false)\n\n  // Para aceitar o arquivo passe `true`, assim:\n  cb(null, true)\n\n  // Você sempre pode passar um erro se algo der errado:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n## Error handling\n\nQuando encontrar um erro, Multer delegará o erro para Express. Você pode exibir uma boa página de erro usando [the standard express way](http://expressjs.com/guide/error-handling.html).\n\nSe você quer pegar erros especificamente do Multer, você pode enviar para o função de middleware. Além disso, se você quiser pegar apenas [os erros do Multer](https://github.com/expressjs/multer/blob/main/lib/multer-error.js), você pode usar a classe `MulterError` que está ligado ao objeto `multer` (e.g. `err instanceof multer.MulterError`).\n\n```javascript\nconst multer = require('multer')\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // Ocorreu um erro durante o upload.\n    } else if (err) {\n      // Ocorreu um erro durante o upload.\n    }\n\n    // Tudo correu bem.\n  })\n})\n```\n\n## Mecanismo de armazenamento personalizado\n\nPara obter informações sobre como criar seu próprio mecanismo de armazenamento, veja [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## Licença\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-ru.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter — это middleware для фреймворка express для обработки `multipart/form-data`, нужная в первую очередь при загрузке файлов. Написана как обертка над [busboy](https://github.com/mscdex/busboy) для ее максимально эффективного использования.\n\n**ВАЖНО**: Multer не обрабатывает никакой другой тип форм, кроме `multipart/form-data`.\n\n## Переводы\n\nЭто README также доступно на других языках:\n\n- [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md) (арабский)\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (Английский)\n- [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) (Испанский)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (Китайский)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (Корейский)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (бр Португальский)\n\n## Установка\n\n```sh\n$ npm install --save multer\n```\n\n## Использование\n\nMulter добавляет объект `body` и объект `file` (или `files`) внутрь объекта `request`. Объект `body` содержит значения текстовых полей формы, объект `file` (`files`) содержит файл или файлы, загружаемые через форму.\n\nПростой пример использования:\n\nНе забывайте про `enctype=\"multipart/form-data\"` в вашей форме.\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nconst express = require('express')\nconst multer  = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file - файл `avatar`\n  // req.body сохранит текстовые поля, если они будут\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files - массив файлов `photos`\n  // req.body сохранит текстовые поля, если они будут\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files - объект (String -> Array), где fieldname - ключ, и значение - массив файлов\n  //\n  // например:\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // req.body сохранит текстовые поля, если они будут\n})\n```\n\nЕсли вам нужно обработать multipart-форму, содержащую только текст, используйте метод `.none()`:\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body содержит текстовые поля\n})\n```\n\n## API\n\n### Информация о файлах\n\nКаждый файл содержит следующую информацию:\n\nКлюч | Описание | Замечания\n--- | --- | ---\n`fieldname` | Имя поля, заданное в форме |\n`originalname` | Имя файла на компьютере пользователя |\n`encoding` | Кодировка файла |\n`mimetype` | Mime-тип файла |\n`size` | Размер файла в байтах |\n`destination` | Каталог, где будет сохранен файл | `DiskStorage`\n`filename` | Имя файла без `destination` | `DiskStorage`\n`path` | Полный путь к загружаемому файлу | `DiskStorage`\n`buffer` | `Buffer` из всего файла | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter принимает объект с опциями. Базовая опция `dest` указывает Multer, куда загружать файлы. Если вы не указываете объект с опциями, файлы будут находиться в памяти и не будут записаны на диск.\n\nПо умолчанию, Multer переименовывает файлы, чтобы избежать конфликтов. Это настраиваемо под ваши потребности.\n\nСледующие опции могут быть переданы Multer.\n\nКлюч | Описание\n--- | ---\n`dest` или `storage` | Где сохранять файлы\n`fileFilter` | Функция для контроля принятия файлов\n`limits` | Ограничения по загрузке\n`preservePath` | Сохранять полный путь к файлам вместо только базового имени\n`defParamCharset` | Кодировка по умолчанию для значений параметров заголовков частей (например, имя файла), которые не являются расширенными параметрами (не содержат явную кодировку). По умолчанию: `'latin1'`\n\nОбычно для веб-приложения нужно обязательно переопределить `dest`, как показано в примере ниже.\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\nЕсли вам нужно больше возможностей для управления приложением, можно использовать `storage` вместо `dest`. Multer поставляется с двумя движками работы с памятью, `DiskStorage` и `MemoryStorage`, другие движки можно найти у сторонних разработчиков.\n\n#### `.single(fieldname)`\n\nПринимает один файл с именем `fieldname`. Файл будет сохранен в `req.file`.\n\n#### `.array(fieldname[, maxCount])`\n\nПринимает массив файлов с именем `fieldname`. Опционально можно задать ошибку при попытке загрузки более `maxCount` файлов. Массив файлов будет сохранен в `req.files`.\n\n#### `.fields(fields)`\n\nПринимает набор файлов, определенных в `fields`. Объект с массивом файлов будет сохранен в `req.files`.\n\n`fields` должен быть массивом объектов с полями `name` и опциональным `maxCount`.\nНапример:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\nПринимает только текстовые поля формы. При попытке загрузки файла падает с ошибкой \"LIMIT\\_UNEXPECTED\\_FILE\".\n\n#### `.any()`\n\nПринимает все переданные файлы. Массив файлов будет сохранен в `req.files`.\n\n**ПРЕДУПРЕЖДЕНИЕ:** Убедитесь в корректной обработке загрузки файлов вашим приложением. Никогда не используйте Multer как middleware глобально, если пользователь может загрузить вредоносные файлы, и тем самым нарушить работу вашего приложения. Используйте этот метод, только если вы полностью управляете процессом загрузки файлов.\n\n### `storage`\n\n#### `DiskStorage`\n\nДвижок дискового пространства. Дает полный контроль над размещением файлов на диск.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now())\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\nДоступно две опции, расположение `destination` и имя файла `filename`. Обе эти функции определяют, где будет находиться файл после загрузки.\n\n`destination` используется, чтобы задать каталог, в котором будут размещены файлы. Может быть задан строкой (например, `'/tmp/uploads'`). Если не задано расположение `destination`, операционная система воспользуется для сохранения каталогом для временных файлов.\n\n**Важно:** Вы должны создать каталог, когда используете `destination`. При передачи в качестве аргумента строки, Multer проверяет, что каталог создан.\n\n`filename` используется, чтобы определить, как будет назван файл внутри каталога. Если\nимя файла `filename` не задано, каждому файлу будет сконфигурировано случайное имя без расширения файла.\n\n**Важно:** Multer не добавляет никакого файлового расширения, ваша функция должна возвращать имя файла с необходимым расширением.\n\nВ аргументах каждой функции прокидывается запрос (`req`) и набор информации о файле (`file`).\n\nОбратите внимание, что `req.body` может быть не полностью заполнено. Это зависит от порядка отправки клиентом полей и файлов на сервер.\n\n#### `MemoryStorage`\n\nДвижок оперативной памяти сохраняет файлы в памяти как объекты типа `Buffer`. В этом случае нет никаких дополнительных опций.\n\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\nКогда вы используете этот тип передачи, информация о файле будет содержать поле `buffer`, которое содержит весь файл.\n\n**ПРЕДУПРЕЖДЕНИЕ**: Загрузка очень больших файлов, или относительно небольших файлов в большом количестве может вызвать переполнение памяти.\n\n### `limits`\n\nОбъект, устанавливающий ограничения. Multer прокидывает этот объект напрямую в busboy, поэтому детали можно посмотреть\n[на странице с методами busboy](https://github.com/mscdex/busboy#busboy-methods).\n\nДоступны следующие целочисленные значения:\n\nКлюч | Описание | Значение по умолчанию\n--- | --- | ---\n`fieldNameSize` | Максимальный размер имени файла | 100 bytes\n`fieldSize` | Максимальный размер значения поля | 1MB\n`fields` | Максимальное количество не-файловых полей | Не ограничено\n`fileSize` | Максимальный размер файла в байтах для multipart-форм | Не ограничен\n`files` | Максимальное количество полей с файлами для multipart-форм | Не ограничено\n`parts` | Максимальное количество полей с файлами для multipart-форм (поля плюс файлы) | Не ограничено\n`headerPairs` | Максимальное количество пар ключ-значение key=>value для multipart-форм, которое обрабатывается | 2000\n\nУстановка ограничений может помочь защитить ваш сайт от DoS-атак.\n\n### `fileFilter`\n\nЗадают функцию для того, чтобы решать, какие файлы будут загружены, а какие — нет. Функция может выглядеть так:\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // Функция должна вызывать `cb` с булевым значением,\n  // которое показывает, следует ли принять файл\n\n  // Чтобы отклонить, прокиньте в аргументы `false` так:\n  cb(null, false)\n\n  // Чтобы принять файл, используется как аргумент `true` таким образом:\n  cb(null, true)\n\n  // Вы можете всегда вернуть ошибку, если что-то пошло не так:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n## Обработка ошибок\n\nКогда выбрасывается исключение, Multer делегирует его обработку Express. Вы можете выводить страницу ошибки [стандартными для express способами](http://expressjs.com/guide/error-handling.html).\n\nЕсли вы хотите отлавливать ошибки конкретно от Multer, вам нужно вызывать собственную middleware для их обработки. Еще, если вы хотите отлавливать [исключительно ошибки Multer](https://github.com/expressjs/multer/blob/main/lib/make-error.js#L1-L9), вы можете использовать класс `MulterError`, который привязан к объекту `multer` (например, `err instanceof multer.MulterError`)\n\n```javascript\nconst multer = require('multer')\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // Случилась ошибка Multer при загрузке.\n    } else {\n      // При загрузке произошла неизвестная ошибка.\n    }\n\n    // Все прекрасно загрузилось.\n  })\n})\n```\n\n## Собственные движки для сохранения файлов\n\nЧтобы получить информацию, как создать собственный движок для обработки загрузки файлов, смотрите страницу [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## Лицензия\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-tr.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter, `multipart/form-data` tipi formları işlemek için kullanılan bir Node.js middleware’idir ve genellikle dosya yükleme işlemlerinde kullanılır.\nMaksimum verimlilik için [busboy](https://github.com/mscdex/busboy) üzerine yazılmıştır.\n\n**NOT:** Multer, `multipart/form-data` olmayan formları işlemeyecektir.\n\n## Çeviriler\n\nBu README dosyası ayrıca diğer dillerde de mevcuttur:\n\n|                                                                                |                 |\n| ------------------------------------------------------------------------------ | --------------- |\n| [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md)      | Arapça          |\n| [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md)  | Çince           |\n| [Français](https://github.com/expressjs/multer/blob/main/doc/README-fr.md)     | Fransızca       |\n| [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md)       | Korece          |\n| [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) | Brezilya Portekizcesi |\n| [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) | Rusça           |\n| [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md)      | İspanyolca      |\n| [O'zbek tili](https://github.com/expressjs/multer/blob/main/doc/README-uz.md)  | Özbekçe         |\n| [Việt Nam](https://github.com/expressjs/multer/blob/main/doc/README-vi.md)     | Vietnamca       |\n| [Türkçe](https://github.com/expressjs/multer/blob/main/doc/README-tr.md)       | Türkçe          |\n\n## Kurulum\n\n```sh\n$ npm install multer\n```\n\n## Kullanım\n\nMulter, `request` nesnesine bir `body` nesnesi ve bir `file` veya `files` nesnesi ekler. `body` nesnesi formdaki metin alanlarının değerlerini içerir; `file` veya `files` nesnesi ise form aracılığıyla yüklenen dosyaları içerir.\n\nTemel kullanım örneği:\n\nFormunuzda enctype=\"multipart/form-data\" eklemeyi unutmayın.\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nconst express = require(\"express\");\nconst multer = require(\"multer\");\nconst upload = multer({ dest: \"uploads/\" });\n\nconst app = express();\n\napp.post(\"/profile\", upload.single(\"avatar\"), function (req, res, next) {\n   // req.file `avatar` dosyasıdır\n  // req.body varsa formdaki metin alanlarını tutar\n});\n\napp.post(\n  \"/photos/upload\",\n  upload.array(\"photos\", 12),\n  function (req, res, next) {\n     // req.files `photos` dosyalarının dizisidir\n    // req.body varsa metin alanlarını içerir\n  }\n);\n\nconst uploadMiddleware = upload.fields([\n  { name: \"avatar\", maxCount: 1 },\n  { name: \"gallery\", maxCount: 8 },\n]);\napp.post(\"/cool-profile\", uploadMiddleware, function (req, res, next) {\n  \n// req.files bir nesnedir (String -> Array) ve burada fieldname (alan adı) anahtar, değer ise dosyaların bulunduğu bir dizidir.\n// Örneğin:\n// req.files['avatar'][0] -> File\n// req.files['gallery'] -> Array\n//\n// req.body varsa metin alanların içerir\n\n});\n```\n\nSadece metin verilerini içeren bir multipart formu işlemek için .none() metodunu kullanabilirsiniz:\n\n```javascript\nconst express = require(\"express\");\nconst app = express();\nconst multer = require(\"multer\");\nconst upload = multer();\n\napp.post(\"/profile\", upload.none(), function (req, res, next) {\n // req.body metin alanlarını içerir\n});\n```\n\nİşte multer'ın HTML formunda nasıl kullanıldığına dair bir örnek. `Enctype=\"multipart/form-data\"` ve `name=\"uploaded_file\"` alanlarına özellikle dikkat edin:\n\n\n\n\n\n```html\n<form action=\"/stats\" enctype=\"multipart/form-data\" method=\"post\">\n  <div class=\"form-group\">\n    <input type=\"file\" class=\"form-control-file\" name=\"uploaded_file\" />\n    <input\n      type=\"text\"\n      class=\"form-control\"\n      placeholder=\"Number of speakers\"\n      name=\"nspeakers\"\n    />\n    <input type=\"submit\" value=\"Get me the stats!\" class=\"btn btn-default\" />\n  </div>\n</form>\n```\n\nArdından JavaScript dosyanızda, hem dosyaya hem de body’ye erişmek için bu satırları eklemeniz gerekir. Upload fonksiyonunuzda formdaki `name` alanı değerini kullanmanız önemlidir. Bu, multer’a request içindeki hangi alanda dosya araması gerektiğini söyler. Eğer bu alanlar HTML formunda ve sunucuda aynı değilse, yükleme başarısız olur.\n\n\n\n\n\n```javascript\nconst multer = require(\"multer\");\nconst upload = multer({ dest: \"./public/data/uploads/\" });\napp.post(\"/stats\", upload.single(\"uploaded_file\"), function (req, res) {\n   // req.file formdaki dosya, burada 'uploaded_file'\n  // req.body metin alanlarını içerir\n  console.log(req.file, req.body);\n});\n```\n\n## API\n\n### Dosya bilgisi\n\nHer dosya aşağıdaki bilgileri içerir:\n\n| Anahtar        | Açıklama                                | Not             |\n| -------------- | -------------------------------------- | --------------- |\n| `fieldname`    | Formdaki alan adı                        |                 |\n| `originalname` | Kullanıcının bilgisayarındaki dosya adı  |                 |\n| `encoding`     | Dosyanın kodlama tipi                    |                 |\n| `mimetype`     | Dosyanın MIME tipi                        |                 |\n| `size`         | Dosyanın boyutu (byte)                   |                 |\n| `destination`  | Dosyanın 'destination' içindeki adı       | `DiskStorage`   |\n| `filename`     | Dosyanın klasör içindeki adı             | `DiskStorage`   |\n| `path`         | Yüklenen dosyanın tam yolu               | `DiskStorage`   |\n| `buffer`       | Dosyanın tamamını içeren `Buffer`       | `MemoryStorage` |\n\n### `multer(opts)`\n\nMulter bir options (ayarlar) nesnesi kabul eder. En temel seçenek `dest` olup, dosyaların nereye yükleneceğini belirtir. Eğer options nesnesi verilmezse dosyalar bellekte tutulur ve diske yazılmaz.\n\nVarsayılan olarak, ad çakışmalarını önlemek için Multer dosyaları yeniden adlandırır. Yeniden adlandırma fonksiyonu ihtiyacınıza göre özelleştirilebilir.\n\nMulter’a geçirilebilecek seçenekler:\n\n\n| Anahtar           | Açıklama                                                |\n| ----------------- | ------------------------------------------------------ |\n| `dest` veya `storage` | Dosyaların nereye kaydedileceği                       |\n| `fileFilter`      | Hangi dosyaların kabul edileceğini kontrol eden fonksiyon |\n| `limits`          | Yüklenen veri sınırları                                 |\n| `preservePath`    | Sadece dosya adı yerine tam yolu saklar                |\n\n\nOrtalama bir web uygulamasında genellikle sadece `dest` gerekir:\n\n```javascript\nconst upload = multer({ dest: \"uploads/\" });\n```\n\nDaha fazla kontrol için `storage` seçeneğini kullanabilirsiniz. `Multer`, `DiskStorage` ve `MemoryStorage` depolama motorları ile gelir; üçüncü taraf motorlar da mevcuttur.\n\n#### `.single(fieldname)`\n\nTek bir dosya kabul eder ve `fieldname` ile eşleşen dosyayı `req.file` içine koyar.\n\n#### `.array(fieldname[, maxCount])`\n\nBirden fazla dosya kabul eder ve `fieldname` ile eşleşenleri `req.files` dizisine koyar. Opsiyonel olarak `maxCount`'tan fazla dosya yüklenirse hata verir.\n\n#### `.fields(fields)`\n\nBelirtilen alanlardan dosya kabul eder. fields bir nesne dizisi olup `req.files` içine koyulur.\n\n`fields`, `name` ve isteğe bağlı olarak `maxCount` içeren nesnelerden oluşan bir dizi olmalıdır.\nÖrnek:\n\n```javascript\n[\n  { name: \"avatar\", maxCount: 1 },\n  { name: \"gallery\", maxCount: 8 },\n];\n```\n\n#### `.none()`\n\nYalnızca metin alanlarını kabul eder. Herhangi bir dosya yüklenirse,\n“LIMIT_UNEXPECTED_FILE” kodlu bir hata verir\n\n#### `.any()`\n\nAğ üzerinden gelen tüm dosyaları kabul eder. Bir dizi dosya\n`req.files` içinde saklanacaktır.\n\n**UYARI:** Kullanıcıların yüklediği dosyaları her zaman kendiniz işlediğinizden emin olun.\nKötü niyetli bir kullanıcı, sizin öngörmediğiniz bir rotaya dosya yükleyebileceğinden, multer'ı asla global bir orta katman yazılımı olarak eklemeyin.\nBu işlevi yalnızca, yüklenen dosyaları işlediğiniz rotalarda kullanın.\n\n\n### `storage`\n\n#### `DiskStorage`\n\nDisk depolama motoru, dosyaları diske kaydetme konusunda size tam kontrol sağlar.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, \"/tmp/my-uploads\");\n  },\n  filename: function (req, file, cb) {\n    const uniqueSuffix = Date.now() + \"-\" + Math.round(Math.random() * 1e9);\n    cb(null, file.fieldname + \"-\" + uniqueSuffix);\n  },\n});\n\nconst upload = multer({ storage: storage });\n```\n\nİki seçenek mevcuttur: `destination` ve `filename`. Her ikisi de\ndosyanın nerede saklanacağını belirleyen işlevlerdir.\n\n`destination`, yüklenen dosyaların hangi klasörde\nsaklanacağını belirlemek için kullanılır. Bu, `string` olarak da verilebilir (ör. `'/tmp/uploads'`). Eğer\n`destination` verilmezse, işletim sisteminin geçici dosyalar için varsayılan\ndizini kullanılır.\n\n**Not:** `destination` işlevini kullanarak dizin oluşturmaktan siz\nsorumlusunuz. Bir string aktardığınızda, multer dizin sizin için\noluşturulduğundan emin olacaktır.\n\n`filename`, dosyanın klasör içinde nasıl adlandırılacağını belirlemek için kullanılır.\n`filename` belirtilmezse, her dosyaya dosya uzantısı içermeyen rastgele bir ad verilir.\n\n**Not:** Multer sizin için herhangi bir dosya uzantısı eklemez, işleviniz\ndosya uzantısı ile birlikte tam bir dosya adı döndürmelidir.\n\nHer fonksiyona, karar vermeyi kolaylaştırmak için hem istek (`req`) hem de dosya\nhakkında bazı bilgiler (`file`) aktarılır.\n\n`req.body`'nin henüz tam olarak doldurulmamış olabileceğini unutmayın. Bu,\nistemcinin alanları ve dosyaları sunucuya aktarma sırasına bağlıdır.\n\nGeri aramada kullanılan çağırma kuralını anlamak için (ilk parametre olarak null geçilmesi gerekir),\n[Node.js hata işleme](https://web.archive.org/web/20220417042018/https://www.joyent.com/node-js/production/design/errors)\n\n#### `MemoryStorage`\n\nBellek depolama motoru, dosyaları bellekte `Buffer` nesneleri olarak depolar.\nHerhangi bir seçeneği yoktur.\n\n```javascript\nconst storage = multer.memoryStorage();\nconst upload = multer({ storage: storage });\n```\n\nBellek depolama kullanıldığında, dosya bilgileri tüm dosyayı içeren\n`buffer` adlı bir alan içerir.\n\n**UYARI**: Çok büyük dosyaları veya nispeten küçük dosyaları çok hızlı bir şekilde\nçok sayıda yüklemek, bellek depolama kullanıldığında uygulamanızın bellek yetersizliği\nsorununa neden olabilir.\n\n### `limits`\n\nAşağıdaki isteğe bağlı özelliklerin boyut sınırlarını belirten bir nesne. Multer bu nesneyi doğrudan busboy'a aktarır ve özelliklerin ayrıntıları [busboy'un sayfasında](https://github.com/mscdex/busboy#busboy-methods) bulunabilir.\n\nKullanılabilir tamsayı değerleri şöyledir:\n\n| Anahtar          | Açıklama                                                                 | Varsayılan  |\n| ---------------- | ------------------------------------------------------------------------ | ------------ |\n| `fieldNameSize`  | Maksimum alan adı boyutu                                                 | 100 bayt     |\n| `fieldSize`      | Maksimum alan değeri boyutu (byte cinsinden)                             | 1MB          |\n| `fields`         | Dosya olmayan alanların maksimum sayısı                                  | Sonsuz       |\n| `fileSize`       | Çok parçalı (multipart) formlar için maksimum dosya boyutu (bayt cinsinden) | Sonsuz       |\n| `files`          | Çok parçalı (multipart) formlar için maksimum dosya alanı sayısı         | Sonsuz       |\n| `parts`          | Çok parçalı (multipart) formlar için toplam parça sayısı (alan + dosya)  | Sonsuz       |\n| `headerPairs`    | Çok parçalı (multipart) formlar için ayrıştırılacak maksimum header anahtar-değer çifti sayısı | 2000          |\n\n\nSınırları belirlemek, sitenizi hizmet reddi (DoS) saldırılarına karşı korumaya yardımcı olabilir.\n\n### `fileFilter`\n\nYüklenecek ve atlanacak dosyaları kontrol etmek için bunu bir işleve ayarlayın.\nİşlev şu şekilde olmalıdır:\n\n```javascript\nfunction fileFilter(req, file, cb) {\n  // Fonksiyon, dosyanın kabul edilip edilmeyeceğini belirtmek için\n  // boolean bir değerle `cb` fonksiyonunu çağırmalıdır.\n\n  // Bu dosyayı reddetmek için false döndürün:\n  cb(null, false);\n\n  // Dosyayı kabul etmek için true döndürün:\n  cb(null, true);\n\n  // Bir hata oluşursa her zaman bir hata da döndürebilirsiniz:\n  cb(new Error(\"Ne olduğunu bilmiyorum!\"));\n}\n```\n\n## Hata Yönetimi \n\nBir hatayla karşılaşıldığında, Multer hatayı Express'e devreder.\n[Standart Express yöntemi](http://expressjs.com/guide/error-handling.html) kullanarak güzel bir hata sayfası görüntüleyebilirsiniz.\n\nÖzellikle Multer'dan gelen hataları yakalamak istiyorsanız,\norta katman işlevini kendiniz çağırabilirsiniz. Ayrıca, yalnızca [Multer hatalarını](https://github.com/expressjs/multer/blob/main/lib/multer-error.js) yakalamak istiyorsanız, `multer` nesnesine eklenmiş olan `MulterError` sınıfını kullanabilirsiniz (ör. `err instanceof multer.MulterError`).\n\n```javascript\nconst multer = require(\"multer\");\nconst upload = multer().single(\"avatar\");\n\napp.post(\"/profile\", function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // Yükleme sırasında bir Multer hatası oluştu\n    } else if (err) {\n      // Yükleme sırasında bilinmeyen bir hata oluştu\n    }\n\n    // Her şey sorunsuz geçti.\n  });\n});\n```\n\n## Özel depolama motoru\n\nKendi depolama motorunuzu nasıl oluşturacağınız hakkında bilgi için [Multer Depolama Motoru](https://github.com/expressjs/multer/blob/main/StorageEngine.md) bölümüne bakın.\n\n## Lisans\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-uz.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter - bu nodejs middleware bo'lib, asosan `multipart/form-data` shaklda yuborilgan fayllarni yuklashda ishlatiladi. Yuqori samaradorlikka erishish uchun [busboy](https://github.com/mscdex/busboy)ning ustiga yozilgan.\n\n**Muhim**: Multer `multipart` bo'lmagan har qanday formani qayta ishlamaydi.\n\n## Tarjimalar\n\nBu README boshqa tillarda ham mavjud:\n\n- [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md) (arabcha)\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (inglizcha)\n- [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) (ispancha)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (xitoycha)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (korescha)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (portugalcha)\n-  [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (ruscha)\n- [Français](https://github.com/expressjs/multer/blob/main/doc/README-fr.md) (fransuzcha)\n\n\n## O'rnatish\n\n```sh\n$ npm install --save multer\n```\n\n## Foydalanish\n\nMulter - `request` ob'ektiga `body` va `file` yoki `files` ob'ektini qo'shadi. `body` ob'ekti formaning matn maydonlarining (fields) qiymatlarini o'z ichiga oladi, `file` yoki `files` ob'ekti forma orqali yuklangan fayllarni o'z ichiga oladi.\n\nSodda ishlatish uchun namuna:\n\nFormada `enctype=\"multipart/form-data\"` qo'shish esdan chiqmasin\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nconst express = require('express')\nconst multer  = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file - fayl `avatar`\n  // req.body agar matnli maydonlar (fields) bo'lsa, ularni saqlanadi\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files - fayllar massivi `photos`\n  // req.body agar matnli maydonlar (fields) bo'lsa, ularni saqlanadi\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files - bu ob'ekt (String -> Array), matn maydoni(fieldname) - bu key, va qiymat - fayllar massivi\n  //\n  // misol:\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // req.body agar matnli maydonlar (fields) bo'lsa, ularni saqlanadi\n})\n```\n\nAgarda siz faqat matndan iborat multipart form bilan ishlashingiz kerak bo'lsa,  `.none()` ishlating:\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body matnli maydonlar (fields)ni o'zida saqlaydi\n})\n```\n\n## API\n\n### Fayl haqida ma'lumot\n\nHar bir fayl quyidagi ma'lumotlarni o'zida saqlaydi:\n\nKalit(key) | Ta'rif                                 | Eslatma\n--- |----------------------------------------| ---\n`fieldname` | Formada berilgan maxsus nom            |\n`originalname` | Foydalanuvchi kompyuteridagi fayl nomi |\n`encoding` | Faylning kodlash turi                  |\n`mimetype` | Faylning `mime` turi                   |\n`size` | Fayl hajmi - baytda                    |\n`destination` | Fayl saqlangan papka                   | `DiskStorage`\n`filename` | `destination`ni ichidagi fayl nomi     | `DiskStorage`\n`path` | Yuklangan faylning to'liq yo'li        | `DiskStorage`\n`buffer` | Butun boshli fayl `Buffer` tipda       | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter qo'shimcha ob'ekt qabul qiladi, ulardan eng asosiysi - `dest`,\nMulterga fayllarni qayerga yuklash kerakligini aytadigan xususiyat. Agarda siz qo'shimcha(`options`) ob'ektni tashlab ketsangiz, fayllar xotirada saqlanadi va hech qachon diskka yozilmaydi.\n\nStandart holatda - Multer nomlashda kelib chiqishi mumkin bo'lgan muammolarni oldini olish uchun fayllar nomini o'zgartiradi. O'z talablaringizga mos ravishda nomlash funksiyasini sozlay olashingiz mumkin.\n\nQuyidagilar Multerga qo'shimcha qiymat sifati berilishi mumkin:\n\nKalit(key) | Ta'rif\n--- | ---\n`dest` yoki `storage` | Faylni qayerda saqlash\n`fileFilter` | Qaysi fayllar qabul qilinishini boshqarish funksiyasi\n`limits` | Yuklash chegarasi\n`preservePath` | Asosiy nom o'rniga fayllarning to'liq yo'lini saqlash\n`defParamCharset` | Kengaytirilgan parametrlar bo'lmagan qism sarlavha parametrlari qiymatlari (masalan, fayl nomi) uchun ishlatish uchun standart belgilar to'plami (aniq belgilar to'plamini o'z ichiga olmaydi). Standart: `'latin1'`\n\nO'rtacha veb-ilovada faqat `dest` kerak bo'lishi mumkin va quyidagicha sozlanishi mumkin\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\nYuklamalaringizni boshqarishda ko'proq nazoratni xohlasangiz, `dest` o'rniga `storage` tanlovini ishlatishingiz kerak. Multer `DiskStorage` va `MemoryStorage` saqlash motorlari(engines) bilan keladi. Boshqa motorlar(engines) uchun uchinchi tomondan(third parties) ko'proq tanlovlar keladi.\n\n#### `.single(fieldname)`\n\n`fieldname` nomi bilan yagona faylni qabul qiladi. Yagona fayl `req.file` da saqlanadi.\n\n#### `.array(fieldname[, maxCount])`\n\n`fieldname` nomi bilan fayllar massivini qabul qiladi. Agar `maxCount` dan ko'p fayl yuklash urinishi bo'lsa, hatolikni aniqlash imkoniyatini berish mumkin. Fayllar massivi `req.files` da saqlanadi.\n\n#### `.fields(fields)`\n\n`fields`da aniqlangan fayllarni qabul qiladi. Fayllar massivini saqlash uchun `req.files` ichidagi massivda saqlanadi.\n\n`fields` ob'ektida `name` va `maxCount` kalitlar(keys)ni o'z ichiga olishi kerak. Misol:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\nFaqatgina matnli maydonlar(fields)ni oladi. Agarda biror fayl yuklansa, \"LIMIT\\_UNEXPECTED\\_FILE\" xatoligi yuboriladi.\n\n#### `.any()`\n\nUshbu so'rov barcha fayllarni qabul qiladi, fayllar `req.files` ichida saqlanadi.\n\n**OGOHLANTIRISH:** Foydalanuvchi yuklagan fayllarni doimo boshqarib turishni unutmang. Ularni boshqa yo'l(route)ni kutmagan holda fayllarini yuklash imkonini beradigan global middleware sifatida multerni sozlamang. Faqatgina yuklangan fayllarni boshqarish kerak bo'lgan yo'l(route)larda ushbu funksiyani ishlating.\n\n### `storage`\n\n#### `DiskStorage`\n\nDiskka saqlash motori(engine) sizga fayllarni saqlashda to'liq nazorat qilish imkonini beradi.\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now())\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\n`destination` va `filename` qo'shimcha tanlovlari mavjud, ular ikkala ham qaysi papkada faylni saqlash kerakligini aniqlab turadigan funksiyalardir.\n\n`destination` yuklangan fayllarni qaysi papkada saqlash kerakligini aniqlab turadi. Bu, `string` sifatida berilishi mumkin (masalan, `'/tmp/uploads'`). Agar `destination` berilmagan bo'lsa, operatsion tizimning vaqtinchalik fayllar uchun ishlatiladigan papkasini ishlatadi.\n\n**Diqqat:** `destination` ni funksiya sifatida berib bo'lganda papka ochilganligiga o'zingiz javobgar bo'lasiz. Agar `string` sifatida bersangiz, multer papkani o'zi uchun yaratishni ta'minlaydi.\n\n`filename` faylni papka ichida qanday nomlanganligini aniqlaydi. Agar `filename` berilmagan bo'lsa, har bir faylga fayl kengaytmasini o'z ichiga olmagan tasodifiy nom beriladi.\n\n**Diqqat:** Multer siz uchun fayl kengaytmasini qo'shmaydi, sizning funksiyangiz kengaytma bilan to'liq nomni qaytarishi kerak.\n\nHar bir funksiya `req` so'rovini va fayl haqida ma'lumotlarni (`file`) olish uchun o'tkaziladi.\n\nDiqqat qiling, `req.body` hali to'liq to'ldirilmagan bo'lishi mumkin. Bu mijozning maydon(field)larni va fayllarni serverga qanday yuborishiga bog'liq bo'ladi.\n\nCallback funktsiyasida ishlatiladigan chaqirish tartibini tushunish uchun (birinchi parametr sifatida null o‘tkazish talab etilishi) ko‘rish uchun quyidagi manzilga murojaat qiling:\n[Node.js da xatoliklarni ushlash](https://web.archive.org/web/20220417042018/https://www.joyent.com/node-js/production/design/errors)\n\n#### `MemoryStorage`\n\nXotira saqlash motori fayllarni xotirada `Buffer` ob'ektlar sifatida saqlaydi. Uning qo'shimcha qiymatlari yo‘q.\n\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\nXotirada saqlash paytida, fayl ma'lumotlari `buffer` deb nomlanadigan maydonni o‘z ichiga oladi.\n\n**DIQQAT:** Juda katta fayllarni yuklash, yoki kichik fayllarni tez-tez yuklash, xotirada saqlash ishlatilganda, sizning ilovangizning xotirasini to'ldirib qo'yishi mumkin.\n\n### `limits`\n\nQuyidagi xususiyatlar o'lchov(limit)larni aniqlaydigan obyekt. Multer ushbu obyektni to'g'ridan-to'g'ri busboy ga o'tkazadi va xususiyatlar tafsilotlari [busboy sahifasida](https://github.com/mscdex/busboy#busboy-methods)dan topishingiz mumkin.\n\nQuyidagi butun qiymatlar mavjud:\n\nKalit(key) | Ta'rif                                                                                      | Odatiy qiymat\n--- |---------------------------------------------------------------------------------------------| ---\n`fieldNameSize` | Maksimal maydon nomi o'lchami                                                               | 100 bayt\n`fieldSize` | Maksimal maydon qiymati o'lchami (baytlarda)                                                | 1MB\n`fields` | Fayl bo'lmagan  maydonlarning maksimal soni                                                 | Cheklanmagan\n`fileSize` | Multipart form uchun faylning maksimal o'lchami (baytda)                        | Cheklanmagan\n`files` | Multipart form uchun fayllar sonining maksimal chegarasi                        | Cheklanmagan\n`parts` | Multipart form uchun fayllar sonining maksimal chegarasi (fieldlar va fayllar)  | Cheklanmagan\n`headerPairs` | Multipart form uchun ma'lumotlar (kalit va qiymat juftliklari) sonining maksimal chegarasi | 2000\n\nChegaralarni sozlash, DoS-hujumlariga qarshi saytingizni himoya qilishga yordam bera olishi mumkin\n\n### `fileFilter`\n\nBu, qaysi fayllarni yuklashi, qaysilarini o'tkazib yuborish kerakligini boshqarish uchun funksiya sifatida sozlasa bo'ladi. Funksiya quyidagi ko'rinishda bo'lishi kerak:\"\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // Bu funksiya, faylni qabul qilish kerakligini anglatish uchun `cb` ni\n  // boolean qiymat bilan chaqirish kerak.\n\n  // Faylni qabul qilishni rad etish uchun false quyudagicha berilishi kerak:\n  cb(null, false)\n\n  // Faylni qabul qiilishni tasdiqlash uchun true quyudagicha berilishi kerak:\n  cb(null, true)\n\n  // Nimadir xato ketsa, siz har doim  Error berishingiz mumkin:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n## Xatolar bilan ishlash\n\nXatoga duch kelganda, Multer xatoni Expressga yuboradi. [standart express usuli](http://expressjs.com/guide/error-handling.html)dan foydalanib xatoni tartibliroq chiqarishingiz mumkin.\n\nAgar siz Multerdan chiqqan xatolarni aniqlamoqchi bo'lsangiz o'zingiz `middleware` funksiya yozishingiz mumkin. Shuningdek, agar siz faqat [Multer xatolarini](https://github.com/expressjs/multer/blob/main/lib/multer-error.js) ushlamoqchi bo'lsangiz, siz `multer` ob'ektiga yozilgan `MulterError` class ni ishlatishingiz mumkin (masalan, `err instanceof multer.MulterError`).\n\n\n```javascript\nconst multer = require('multer')\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // Yuklanishda Multerdan xatolik yuz berganda.\n    } else {\n      // Yuklanishda noma'lum xatolik yuz berganda.\n    }\n\n    // Hammasi muvaffaqqiyatli bo'lganda.\n  })\n})\n```\n\n## Maxsus saqlash mexanizmi\n\nO'zingizning saqlash dvigatelingizni qanday yaratish haqida ma'lumot olish: [Maxsus saqlash mexanizmi](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## Litsenziya\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-vi.md",
    "content": "# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter là thư viện trung gian hỗ trợ việc xử lý `multipart/form-data`, mục đích chính cho việc upload file. Thư viện này dựa trên [busboy](https://github.com/mscdex/busboy) để hiệu quả hơn.\n\n**CHÚ Ý**: Multer sẽ không xử lý bất kỳ form nào ngoài multipart (`multipart/form-data`).\n\n## Dịch:\n\nCác bạn có thể đọc ở các bản dịch ngôn ngữ khác:\n\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (Tiếng Anh)\n- [简体中文](https://github.com/expressjs/multer/blob/main/doc/README-zh-cn.md) (Chinese)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (Korean)\n- [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (Russian)\n\n## Cài đặt\n\n```sh\n$ npm install --save multer\n```\n\n## Sử dụng\n\nMulter gắn thêm một object `body` và một object `file` (hoặc `files` trường hợp upload nhiều file) vào object `request`. Object `body` này sẽ chứa các biến text của form, còn object `file` (hoặc `files`) sẽ chứa các file được upload qua form.\n\nCách sử sụng:\n\nPhải thêm `enctype=\"multipart/form-data\"` vào form của bạn.\n\n```html\n<form action=\"/profile\" method=\"post\" enctype=\"multipart/form-data\">\n  <input type=\"file\" name=\"avatar\" />\n</form>\n```\n\n```javascript\nvar express = require('express');\nvar multer = require('multer');\nvar upload = multer({ dest: 'uploads/' });\n\nvar app = express();\n\napp.post('/profile', upload.single('avatar'), function(req, res, next) {\n  // req.file là 1 file `avatar` được upload\n  // req.body sẽ giữ thông tin gắn kèm (vd: text fields), nếu có\n});\n\napp.post('/photos/upload', upload.array('photos', 12), function(\n  req,\n  res,\n  next\n) {\n  // req.files là một mảng của các file `photos`\n  // req.body sẽ giữ thông tin gắn kèm (vd: text fields), nếu có\n});\n\nvar uploadMiddleware = upload.fields([\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 },\n]);\napp.post('/cool-profile', uploadMiddleware, function(req, res, next) {\n  // req.files là một object kiểu (String -> Array) mà fieldname là key, và value là mảng các files\n  //\n  // vd:\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // req.body sẽ giữ thông tin gắn kèm (vd: text fields), nếu có\n});\n```\n\nTrong trường hợp bạn cần xử lý một multipart form chỉ chứa text, bạn nên sử dụng hàm `.none()`:\n\n```javascript\nvar express = require('express');\nvar app = express();\nvar multer = require('multer');\nvar upload = multer();\n\napp.post('/profile', upload.none(), function(req, res, next) {\n  // req.body sẽ giữ thông tin gắn kèm (vd: text fields)\n});\n```\n\n## API\n\n### Thông tin File được upload\n\nMỗi file sẽ chứa các thông tin sau:\n\n| Thuộc tính     | Mô tả                                                           | Ghi chú                   |\n| -------------- | --------------------------------------------------------------- | ------------------------- |\n| `fieldname`    | tên mỗi thuộc tính ở trong form                                 |\n| `originalname` | Tên của file nằm trên máy của người dùng, trước khi được upload |\n| `encoding`     | Kiểu Encoding của file                                          |\n| `mimetype`     | Mime type của file                                              | `image/jpeg`, `image/png` |\n| `size`         | Kích thước của file (theo bytes)                                |\n| `destination`  | Đường dẫn tới thư mục file được lưu                             | `DiskStorage`             |\n| `filename`     | Tên của file (ở trong `destination`)                            | `DiskStorage`             |\n| `path`         | Đường dẫn đầy đủ tới file đã upload                             | `DiskStorage`             |\n| `buffer`       | Một `Buffer` của toàn bộ file                                   | `MemoryStorage`           |\n\n### Tham số `multer(opts)`\n\nMulter chấp nhận một biến options. Cơ bản là thuộc tính `dest`, là nơi sẽ lưu\nfile được upload. Trong trường hợp bỏ qua options này, file sẽ được giữ trong\nRAM và không được lưu trên ổ cứng.\n\nMặc định, Multer sẽ đổi tên các file, vì vậy để tránh bị trùng lặp, bạn có thể\ntùy biến hàm đổi tên này.\n\nDưới đây là các tùy chọn mà bạn có thể sử dụng:\n\n| Thuộc tính            | Mô tả                                              |\n| --------------------- | -------------------------------------------------- |\n| `dest` hoặc `storage` | Nơi lưu trữ file                                   |\n| `fileFilter`          | Hàm để xử lý chỉ những file nào mới được chấp nhận |\n| `limits`              | Giới hạn dung lượng file được upload               |\n| `preservePath`        | Giữ đầy đủ đường dẫn tới file thay vì chỉ tên file |\n| `defParamCharset`     | Bộ ký tự mặc định để sử dụng cho các giá trị tham số tiêu đề phần (ví dụ: tên tệp) không phải là tham số mở rộng (không chứa bộ ký tự rõ ràng). Mặc định: `'latin1'` |\n\nNói chung với web app, chỉ `dest` mới cần khai báo, như bên dưới:\n\n```javascript\nvar upload = multer({ dest: 'uploads/' });\n```\n\nNếu bạn muốn tùy biến việc upload, bạn sẽ muốn dùng tùy chọn `storage` thay vì `dest`.\nMulter sẽ sử dụng 1 trong 2 cách `DiskStorage` và `MemoryStorage`; Hoặc các cách khác (với các thư viện ngoài).\n\n#### `.single(fieldname)`\n\nChấp nhận chỉ một file với tên thuộc tính `fieldname`. File này truy cập qua `req.file`.\n\n#### `.array(fieldname[, maxCount])`\n\nChấp nhận mảng các file, tất cả đều với tên `fieldname`. Một lỗi sẽ bắn ra nếu có\nnhiều hơn `maxCount` file được upload. Các file này được lưu ở `req.files`.\n\n#### `.fields(fields)`\n\nChấp nhận nhiều file với thuộc tính `fields`. Một object với mảng các file được lưu ở `req.files`.\n\n`fields` là một mảng các object với thuộc tính `name` và có thể có thuộc tính `maxCount` hoặc không.\n\nVí dụ:\n\n```javascript\n[{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }];\n```\n\n#### `.none()`\n\nChỉ chấp nhận các giá trị text trong form. Nếu bất kỳ file được đính\nkèm, một lỗi với mã \"LIMIT_UNEXPECTED_FILE\" sẽ bắn ra.\n\n#### `.any()`\n\nChấp nhận tất cả file đến từ bất kỳ nguồn nào. Một mảng các file sẽ được lưu\nở `req.files`.\n\n**CHÚ Ý:** Hãy chắc chắn bạn không bỏ qua bất kỳ file nào mà người dùng upload.\nĐừng bao giờ khai báo Multer như một middleware toàn cục, vì người dùng có thể upload\ncác file tới một api nào đó mà bạn không biết. Chỉ sử dụng hàm này ở trên api mà bạn\nmuốn xử lý việc upload file.\n\n### `storage`\n\n#### `DiskStorage`\n\nCơ chế lưu trữ trên ổ đĩa cho phép bạn có đầy đủ quyền để thao tác với file.\n\n```javascript\nvar storage = multer.diskStorage({\n  destination: function(req, file, cb) {\n    cb(null, '/tmp/my-uploads');\n  },\n  filename: function(req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now());\n  },\n});\n\nvar upload = multer({ storage: storage });\n```\n\nCó 2 tùy chọn, `destination` và `filename`. Chúng đều dùng để xác định nơi nào\nfile sẽ được lưu trữ.\n\n`destination` được dùng để xác định thư mục nào file được upload. Có thể là một\n`string` (vd: `'/tmp/uploads'`). Nếu không khai báo `destination`, thư mục tạm\n(của hệ điều hành) sẽ được dùng để chứa các file đó.\n\n**Ghi chú:** Nếu bạn khai báo `destination` là một hàm, bạn phải tự tạo đường\ndẫn. Còn nếu truyền vào một string, multer sẽ đảm bảo việc tạo đường dẫn đó cho bạn.\n\n`filename` được dùng để xác định file nào sẽ được lưu trong thư mục. Nếu không\ncó `filename` nào, mỗi file sẽ nhận tên ngẫu nhiên mà không bao gồm đuôi của file.\n\n**Ghi chú:** Multer sẽ không thêm bất kỳ đuôi file nào cho bạn, hàm của bạn nên\ntrả về một file với đuôi của nó.\n\nMỗi hàm được truyền cả ở request (`req`) và thông tin về file (`file`) để xử lý.\n\nChú ý `req.body` có thể không chứa đầy đủ thông tin, phụ thuộc việc thứ tự các\ntrường dữ liệu và file được gửi tới server lúc nào.\n\n#### `MemoryStorage`\n\nMemory storage lưu các file ở bộ nhớ máy dưới dạng một object `Buffer`. Nó không\ncó bất kỳ tùy chọn nào.\n\n```javascript\nvar storage = multer.memoryStorage();\nvar upload = multer({ storage: storage });\n```\n\nKhi sử dụng memory storage, thông tin file sẽ chứa một trường `buffer`, trường\nnày chứa toàn bộ file.\n\n**CHÚ Ý**: Việc upload file rất lớn, hoặc tương tự việc nhiều file nhỏ, có thể\ngây ra tràn bộ nhớ khi memory storage được sử dụng.\n\n### `limits`\n\nMột object mô tả giới hạn kích thước trong thuộc tính nên được sử dụng. Multer truyền object này trực tiếp vào busboy, và chi tiết của busboy có thể xem thêm ở [busboy's page](https://github.com/mscdex/busboy#busboy-methods).\n\nCác số dưới dây cũng có thể được dùng:\n\n| Thuộc tính      | Mô tả                                                                | Giá trị mặc định |\n| --------------- | -------------------------------------------------------------------- | ---------------- |\n| `fieldNameSize` | Độ dài tối đa của tên field                                          | 100 bytes        |\n| `fieldSize`     | Kích thước tối đa của mỗi field (theo bytes)                         | 1MB              |\n| `fields`        | Số lượng tối đa của các fields không phải là file                    | Infinity         |\n| `fileSize`      | Cho multipart forms, kích thước tối đa của file (theo bytes)         | Infinity         |\n| `files`         | Cho multipart forms, số lượng file tối đa                            | Infinity         |\n| `parts`         | Cho multipart forms, số lượng tối đa của parts (gồm fields + files)  | Infinity         |\n| `headerPairs`   | Cho multipart forms, số tối đa trong header cặp key=>value để truyền | 2000             |\n\nKhai báo các giới hạn này giúp cho site của bạn chống lại các tấn công nguy hiểm (DoS).\n\n### `fileFilter`\n\nDùng hàm này để xử lý các file nào cho phép và bị bỏ qua. Xem ví dụ dưới dây:\n\n```javascript\nfunction fileFilter(req, file, cb) {\n  // hàm này sẽ gọi callback `cb` với 1 biến boolean\n  // để chỉ ra rằng file có được chấp nhận hay không\n\n  // Để chặn file này, truyền `false` như sau:\n  cb(null, false);\n\n  // Để chấp nhận file này, truỳen `true`, như sau:\n  cb(null, true);\n\n  // Hoặc bạn có thể truyền vào 1 lỗi nếu có vấn đề xảy ra:\n  cb(new Error(\"I don't have a clue!\"));\n}\n```\n\n## Error handling\n\nKhi một lỗi xảy ra, Multer sẽ gửi lỗi đó cho Express. Bạn có thể hiển thị\nđẹp hơn sử dụng [cách bắt lỗi chuẩn của Express](http://expressjs.com/guide/error-handling.html).\n\nNếu bạn muốn bắt các lỗi cụ thể từ Multer, bạn có thể tự gọi hàm trung gian (middleware) này. Ngoài ra, nếu bạn chỉ muốn bắt [lỗi của Multer](https://github.com/expressjs/multer/blob/main/lib/multer-error.js), bạn có thể dùng class `MulterError` được đính kèm với chính object `multer` (vd: `err instanceof multer.MulterError`).\n\n```javascript\nvar multer = require('multer');\nvar upload = multer().single('avatar');\n\napp.post('/profile', function(req, res) {\n  upload(req, res, function(err) {\n    if (err instanceof multer.MulterError) {\n      // Một lỗi của Multer xảy ra khi upload.\n    } else if (err) {\n      // Một lỗi không xác định xảy ra khi upload.\n    }\n\n    // Mọi thứ khác chạy ok.\n  });\n});\n```\n\n## Tùy chọn storage engine\n\nĐể làm sao tự xây dựng cơ chế lưu file riêng của mình, hãy xem [Multer Storage Engine](https://github.com/expressjs/multer/blob/main/StorageEngine.md).\n\n## License\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "doc/README-zh-cn.md",
    "content": "**此文档于2016年10月3日翻译时multer的版本是1.2.0，它可能不是最新的！**\n**甚至可能存在翻译错误！你可能需要阅读原版英语[README](../README.md)**\n**此文档仅供参考！**\n\n# Multer [![NPM Version][npm-version-image]][npm-url] [![NPM Downloads][npm-downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Test Coverage][test-image]][test-url] [![OpenSSF Scorecard Badge][ossf-scorecard-badge]][ossf-scorecard-visualizer]\n\nMulter 是一个 node.js 中间件，用于处理 `multipart/form-data` 类型的表单数据，它主要用于上传文件。它是写在 [busboy](https://github.com/mscdex/busboy) 之上非常高效。\n\n**注意**: Multer 不会处理任何非 `multipart/form-data` 类型的表单数据。\n\n## 其它语言\n\n- [العربية](https://github.com/expressjs/multer/blob/main/doc/README-ar.md) (阿拉伯语)\n- [English](https://github.com/expressjs/multer/blob/main/README.md) (英语)\n- [Español](https://github.com/expressjs/multer/blob/main/doc/README-es.md) (西班牙文)\n- [한국어](https://github.com/expressjs/multer/blob/main/doc/README-ko.md) (朝鲜语)\n- [Русский язык](https://github.com/expressjs/multer/blob/main/doc/README-ru.md) (俄語)\n- [Português](https://github.com/expressjs/multer/blob/main/doc/README-pt-br.md) (巴西葡萄牙语)\n\n## 安装\n\n```sh\n$ npm install --save multer\n```\n\n## 使用\n\nMulter 会添加一个 `body` 对象 以及 `file` 或 `files` 对象 到 express 的 `request` 对象中。\n`body` 对象包含表单的文本域信息，`file` 或 `files` 对象包含对象表单上传的文件信息。\n\n基本使用方法:\n\n```javascript\nconst express = require('express')\nconst multer  = require('multer')\nconst upload = multer({ dest: 'uploads/' })\n\nconst app = express()\n\napp.post('/profile', upload.single('avatar'), function (req, res, next) {\n  // req.file 是 `avatar` 文件的信息\n  // req.body 将具有文本域数据，如果存在的话\n})\n\napp.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {\n  // req.files 是 `photos` 文件数组的信息\n  // req.body 将具有文本域数据，如果存在的话\n})\n\nconst uploadMiddleware = upload.fields([{ name: 'avatar', maxCount: 1 }, { name: 'gallery', maxCount: 8 }])\napp.post('/cool-profile', uploadMiddleware, function (req, res, next) {\n  // req.files 是一个对象 (String -> Array) 键是文件名，值是文件数组\n  //\n  // 例如：\n  //  req.files['avatar'][0] -> File\n  //  req.files['gallery'] -> Array\n  //\n  // req.body 将具有文本域数据，如果存在的话\n})\n```\n\n如果你需要处理一个只有文本域的表单，你应当使用 `.none()`:\n\n```javascript\nconst express = require('express')\nconst app = express()\nconst multer  = require('multer')\nconst upload = multer()\n\napp.post('/profile', upload.none(), function (req, res, next) {\n  // req.body 包含文本域\n})\n```\n\n## API\n\n### 文件信息\n\n每个文件具有下面的信息:\n\nKey | Description | Note\n--- | --- | ---\n`fieldname` | Field name 由表单指定 |\n`originalname` | 用户计算机上的文件的名称 |\n`encoding` | 文件编码 |\n`mimetype` | 文件的 MIME 类型 |\n`size` | 文件大小（字节单位） |\n`destination` | 保存路径 | `DiskStorage`\n`filename` | 保存在 `destination` 中的文件名 | `DiskStorage`\n`path` | 已上传文件的完整路径 | `DiskStorage`\n`buffer` | 一个存放了整个文件的 `Buffer`  | `MemoryStorage`\n\n### `multer(opts)`\n\nMulter 接受一个 options 对象，其中最基本的是 `dest` 属性，这将告诉 Multer 将上传文件保存在哪。如果你省略 options 对象，这些文件将保存在内存中，永远不会写入磁盘。\n\n为了避免命名冲突，Multer 会修改上传的文件名。这个重命名功能可以根据您的需要定制。\n\n以下是可以传递给 Multer 的选项。\n\nKey | Description\n--- | ---\n`dest` or `storage` | 在哪里存储文件\n`fileFilter` | 文件过滤器，控制哪些文件可以被接受\n`limits` | 限制上传的数据\n`preservePath` | 保存包含文件名的完整文件路径\n`defParamCharset` | 用于部分标头参数值（例如文件名）的默认字符集，这些参数不是扩展参数（不包含显式字符集）。默认值：`'latin1'`\n\n通常，一般的网页应用，只需要设置 `dest` 属性，像这样：\n\n```javascript\nconst upload = multer({ dest: 'uploads/' })\n```\n\n如果你想在上传时进行更多的控制，你可以使用 `storage` 选项替代 `dest`。Multer 具有 `DiskStorage` 和 `MemoryStorage` 两个存储引擎；另外还可以从第三方获得更多可用的引擎。\n\n#### `.single(fieldname)`\n\n接受一个以 `fieldname` 命名的文件。这个文件的信息保存在 `req.file`。\n\n#### `.array(fieldname[, maxCount])`\n\n接受一个以 `fieldname` 命名的文件数组。可以配置 `maxCount` 来限制上传的最大数量。这些文件的信息保存在 `req.files`。\n\n#### `.fields(fields)`\n\n接受指定 `fields` 的混合文件。这些文件的信息保存在 `req.files`。\n\n`fields` 应该是一个对象数组，应该具有 `name` 和可选的 `maxCount` 属性。\n\nExample:\n\n```javascript\n[\n  { name: 'avatar', maxCount: 1 },\n  { name: 'gallery', maxCount: 8 }\n]\n```\n\n#### `.none()`\n\n只接受文本域。如果任何文件上传到这个模式，将发生 \"LIMIT\\_UNEXPECTED\\_FILE\" 错误。这和 `upload.fields([])` 的效果一样。\n\n#### `.any()`\n\n接受一切上传的文件。文件数组将保存在 `req.files`。\n\n**警告:** 确保你总是处理了用户的文件上传。\n永远不要将 multer 作为全局中间件使用，因为恶意用户可以上传文件到一个你没有预料到的路由，应该只在你需要处理上传文件的路由上使用。\n\n### `storage`\n\n#### 磁盘存储引擎 (`DiskStorage`)\n\n磁盘存储引擎可以让你控制文件的存储。\n\n```javascript\nconst storage = multer.diskStorage({\n  destination: function (req, file, cb) {\n    cb(null, '/tmp/my-uploads')\n  },\n  filename: function (req, file, cb) {\n    cb(null, file.fieldname + '-' + Date.now())\n  }\n})\n\nconst upload = multer({ storage: storage })\n```\n\n有两个选项可用，`destination` 和 `filename`。他们都是用来确定文件存储位置的函数。\n\n`destination` 是用来确定上传的文件应该存储在哪个文件夹中。也可以提供一个 `string` (例如 `'/tmp/uploads'`)。如果没有设置 `destination`，则使用操作系统默认的临时文件夹。\n\n**注意:** 如果你提供的 `destination` 是一个函数，你需要负责创建文件夹。当提供一个字符串，multer 将确保这个文件夹是你创建的。\n\n`filename` 用于确定文件夹中的文件名的确定。 如果没有设置 `filename`，每个文件将设置为一个随机文件名，并且是没有扩展名的。\n\n**注意:** Multer 不会为你添加任何扩展名，你的程序应该返回一个完整的文件名。\n\n每个函数都传递了请求对象 (`req`) 和一些关于这个文件的信息 (`file`)，有助于你的决定。\n\n注意 `req.body` 可能还没有完全填充，这取决于向客户端发送字段和文件到服务器的顺序。\n\n#### 内存存储引擎 (`MemoryStorage`)\n\n内存存储引擎将文件存储在内存中的 `Buffer` 对象，它没有任何选项。\n\n```javascript\nconst storage = multer.memoryStorage()\nconst upload = multer({ storage: storage })\n```\n\n当使用内存存储引擎，文件信息将包含一个 `buffer` 字段，里面包含了整个文件数据。\n\n**警告**: 当你使用内存存储，上传非常大的文件，或者非常多的小文件，会导致你的应用程序内存溢出。\n\n### `limits`\n一个对象，指定一些数据大小的限制。Multer 通过这个对象使用 busboy，详细的特性可以在 [busboy's page](https://github.com/mscdex/busboy#busboy-methods) 找到。\n\n可以使用下面这些:\n\nKey | Description | Default\n--- | --- | ---\n`fieldNameSize` | field 名字最大长度 | 100 bytes\n`fieldSize` | field 值的最大长度  | 1MB\n`fields` | 非文件 field 的最大数量 | 无限\n`fileSize` | 在 multipart 表单中，文件最大长度 (字节单位) | 无限\n`files` | 在 multipart 表单中，文件最大数量 | 无限\n`parts` | 在 multipart 表单中，part 传输的最大数量(fields + files) | 无限\n`headerPairs` | 在 multipart 表单中，键值对最大组数 | 2000\n\n设置 limits 可以帮助保护你的站点抵御拒绝服务 (DoS) 攻击。\n\n### `fileFilter`\n设置一个函数来控制什么文件可以上传以及什么文件应该跳过，这个函数应该看起来像这样：\n\n```javascript\nfunction fileFilter (req, file, cb) {\n\n  // 这个函数应该调用 `cb` 用boolean值来\n  // 指示是否应接受该文件\n\n  // 拒绝这个文件，使用`false`，像这样:\n  cb(null, false)\n\n  // 接受这个文件，使用`true`，像这样:\n  cb(null, true)\n\n  // 如果有问题，你可以总是这样发送一个错误:\n  cb(new Error('I don\\'t have a clue!'))\n\n}\n```\n\n## 错误处理机制\n\n当遇到一个错误，multer 将会把错误发送给 express。你可以使用一个比较好的错误展示页 ([express标准方式](http://expressjs.com/guide/error-handling.html))。\n\n如果你想捕捉 multer 发出的错误，你可以自己调用中间件程序。如果你想捕捉 [Multer 错误](https://github.com/expressjs/multer/blob/main/lib/multer-error.js)，你可以使用 `multer` 对象下的 `MulterError` 类 (即 `err instanceof multer.MulterError`)。\n\n```javascript\nconst multer = require('multer')\nconst upload = multer().single('avatar')\n\napp.post('/profile', function (req, res) {\n  upload(req, res, function (err) {\n    if (err instanceof multer.MulterError) {\n      // 发生错误\n    } else if (err) {\n      // 发生错误\n    }\n\n    // 一切都好\n  })\n})\n```\n\n## 定制存储引擎\n\n如果你想要构建自己的存储引擎，请看 [这里](/StorageEngine.md) 。\n\n## License\n\n[MIT](LICENSE)\n\n[ci-image]: https://github.com/expressjs/multer/actions/workflows/ci.yml/badge.svg\n[ci-url]: https://github.com/expressjs/multer/actions/workflows/ci.yml\n[test-url]: https://coveralls.io/r/expressjs/multer?branch=main\n[test-image]: https://badgen.net/coveralls/c/github/expressjs/multer/main\n[npm-downloads-image]: https://badgen.net/npm/dm/multer\n[npm-url]: https://npmjs.org/package/multer\n[npm-version-image]: https://badgen.net/npm/v/multer\n[ossf-scorecard-badge]: https://api.scorecard.dev/projects/github.com/expressjs/multer/badge\n[ossf-scorecard-visualizer]: https://ossf.github.io/scorecard-visualizer/#/projects/github.com/expressjs/multer\n"
  },
  {
    "path": "index.js",
    "content": "var makeMiddleware = require('./lib/make-middleware')\n\nvar diskStorage = require('./storage/disk')\nvar memoryStorage = require('./storage/memory')\nvar MulterError = require('./lib/multer-error')\n\nfunction allowAll (req, file, cb) {\n  cb(null, true)\n}\n\nfunction Multer (options) {\n  if (options.storage) {\n    this.storage = options.storage\n  } else if (options.dest) {\n    this.storage = diskStorage({ destination: options.dest })\n  } else {\n    this.storage = memoryStorage()\n  }\n\n  this.limits = options.limits\n  this.preservePath = options.preservePath\n  this.defParamCharset = options.defParamCharset || 'latin1'\n  this.fileFilter = options.fileFilter || allowAll\n}\n\nMulter.prototype._makeMiddleware = function (fields, fileStrategy) {\n  function setup () {\n    var fileFilter = this.fileFilter\n    var filesLeft = Object.create(null)\n\n    fields.forEach(function (field) {\n      if (typeof field.maxCount === 'number') {\n        filesLeft[field.name] = field.maxCount\n      } else {\n        filesLeft[field.name] = Infinity\n      }\n    })\n\n    function wrappedFileFilter (req, file, cb) {\n      if ((filesLeft[file.fieldname] || 0) <= 0) {\n        return cb(new MulterError('LIMIT_UNEXPECTED_FILE', file.fieldname))\n      }\n\n      filesLeft[file.fieldname] -= 1\n      fileFilter(req, file, cb)\n    }\n\n    return {\n      limits: this.limits,\n      preservePath: this.preservePath,\n      defParamCharset: this.defParamCharset,\n      storage: this.storage,\n      fileFilter: wrappedFileFilter,\n      fileStrategy: fileStrategy\n    }\n  }\n\n  return makeMiddleware(setup.bind(this))\n}\n\nMulter.prototype.single = function (name) {\n  return this._makeMiddleware([{ name: name, maxCount: 1 }], 'VALUE')\n}\n\nMulter.prototype.array = function (name, maxCount) {\n  return this._makeMiddleware([{ name: name, maxCount: maxCount }], 'ARRAY')\n}\n\nMulter.prototype.fields = function (fields) {\n  return this._makeMiddleware(fields, 'OBJECT')\n}\n\nMulter.prototype.none = function () {\n  return this._makeMiddleware([], 'NONE')\n}\n\nMulter.prototype.any = function () {\n  function setup () {\n    return {\n      limits: this.limits,\n      preservePath: this.preservePath,\n      defParamCharset: this.defParamCharset,\n      storage: this.storage,\n      fileFilter: this.fileFilter,\n      fileStrategy: 'ARRAY'\n    }\n  }\n\n  return makeMiddleware(setup.bind(this))\n}\n\nfunction multer (options) {\n  if (options === undefined) {\n    return new Multer({})\n  }\n\n  if (typeof options === 'object' && options !== null) {\n    return new Multer(options)\n  }\n\n  throw new TypeError('Expected object for argument options')\n}\n\nmodule.exports = multer\nmodule.exports.diskStorage = diskStorage\nmodule.exports.memoryStorage = memoryStorage\nmodule.exports.MulterError = MulterError\n"
  },
  {
    "path": "lib/counter.js",
    "content": "var EventEmitter = require('events').EventEmitter\n\nfunction Counter () {\n  EventEmitter.call(this)\n  this.value = 0\n}\n\nCounter.prototype = Object.create(EventEmitter.prototype)\n\nCounter.prototype.increment = function increment () {\n  this.value++\n}\n\nCounter.prototype.decrement = function decrement () {\n  if (--this.value === 0) this.emit('zero')\n}\n\nCounter.prototype.isZero = function isZero () {\n  return (this.value === 0)\n}\n\nCounter.prototype.onceZero = function onceZero (fn) {\n  if (this.isZero()) return fn()\n\n  this.once('zero', fn)\n}\n\nmodule.exports = Counter\n"
  },
  {
    "path": "lib/file-appender.js",
    "content": "function arrayRemove (arr, item) {\n  var idx = arr.indexOf(item)\n  if (~idx) arr.splice(idx, 1)\n}\n\nfunction FileAppender (strategy, req) {\n  this.strategy = strategy\n  this.req = req\n\n  switch (strategy) {\n    case 'NONE': break\n    case 'VALUE': break\n    case 'ARRAY': req.files = []; break\n    case 'OBJECT': req.files = Object.create(null); break\n    default: throw new Error('Unknown file strategy: ' + strategy)\n  }\n}\n\nFileAppender.prototype.insertPlaceholder = function (file) {\n  var placeholder = {\n    fieldname: file.fieldname\n  }\n\n  switch (this.strategy) {\n    case 'NONE': break\n    case 'VALUE': break\n    case 'ARRAY': this.req.files.push(placeholder); break\n    case 'OBJECT':\n      if (this.req.files[file.fieldname]) {\n        this.req.files[file.fieldname].push(placeholder)\n      } else {\n        this.req.files[file.fieldname] = [placeholder]\n      }\n      break\n  }\n\n  return placeholder\n}\n\nFileAppender.prototype.removePlaceholder = function (placeholder) {\n  switch (this.strategy) {\n    case 'NONE': break\n    case 'VALUE': break\n    case 'ARRAY': arrayRemove(this.req.files, placeholder); break\n    case 'OBJECT':\n      if (this.req.files[placeholder.fieldname].length === 1) {\n        delete this.req.files[placeholder.fieldname]\n      } else {\n        arrayRemove(this.req.files[placeholder.fieldname], placeholder)\n      }\n      break\n  }\n}\n\nFileAppender.prototype.replacePlaceholder = function (placeholder, file) {\n  if (this.strategy === 'VALUE') {\n    this.req.file = file\n    return\n  }\n\n  delete placeholder.fieldname\n  Object.assign(placeholder, file)\n}\n\nmodule.exports = FileAppender\n"
  },
  {
    "path": "lib/make-middleware.js",
    "content": "var is = require('type-is')\nvar Busboy = require('busboy')\nvar appendField = require('append-field')\n\nvar Counter = require('./counter')\nvar MulterError = require('./multer-error')\nvar FileAppender = require('./file-appender')\nvar removeUploadedFiles = require('./remove-uploaded-files')\n\nfunction drainStream (stream) {\n  stream.on('readable', () => {\n    while (stream.read() !== null) {}\n  })\n}\n\nfunction makeMiddleware (setup) {\n  return function multerMiddleware (req, res, next) {\n    if (!is(req, ['multipart'])) return next()\n\n    var options = setup()\n\n    var limits = options.limits\n    var storage = options.storage\n    var fileFilter = options.fileFilter\n    var fileStrategy = options.fileStrategy\n    var preservePath = options.preservePath\n    var defParamCharset = options.defParamCharset\n\n    req.body = Object.create(null)\n\n    var busboy\n    var appender = null\n    var isDone = false\n    var readFinished = false\n    var errorOccured = false\n    var pendingWrites = new Counter()\n    var uploadedFiles = []\n\n    function done (err) {\n      var called = false\n      function onFinished () {\n        if (called) return\n        called = true\n        next(err)\n      }\n\n      if (isDone) return\n      isDone = true\n      if (busboy) {\n        req.unpipe(busboy)\n        setImmediate(() => {\n          busboy.removeAllListeners()\n        })\n      }\n      drainStream(req)\n      req.resume()\n\n      // - if responding with an error, drain request body before calling\n      //     next(err) -- avoids EPIPE on the client (server closing connection\n      //     while the client is still sending the request body)\n      // - also listen for 'close' so we don't hang when the client aborts (stream may never 'end')\n      // - skip waiting if the stream is already destroyed (e.g. client aborted)\n      if (err && req.readable && !req.destroyed) {\n        req.once('end', onFinished)\n        req.once('error', onFinished)\n        req.once('close', onFinished)\n        return\n      }\n      next(err)\n    }\n\n    function indicateDone () {\n      if (readFinished && pendingWrites.isZero() && !errorOccured) done()\n    }\n\n    function abortWithError (uploadError, skipPendingWait) {\n      if (errorOccured) return\n      errorOccured = true\n\n      function finishAbort () {\n        function remove (file, cb) {\n          storage._removeFile(req, file, cb)\n        }\n\n        removeUploadedFiles(uploadedFiles, remove, function (err, storageErrors) {\n          if (err) return done(err)\n\n          uploadError.storageErrors = storageErrors\n          done(uploadError)\n        })\n      }\n\n      if (skipPendingWait) {\n        finishAbort()\n      } else {\n        pendingWrites.onceZero(finishAbort)\n      }\n    }\n\n    function abortWithCode (code, optionalField) {\n      abortWithError(new MulterError(code, optionalField))\n    }\n\n    function handleRequestFailure (err) {\n      if (isDone) return\n      if (busboy) {\n        req.unpipe(busboy)\n        busboy.destroy(err)\n      }\n      abortWithError(err, true)\n    }\n\n    req.on('error', function (err) {\n      handleRequestFailure(err || new Error('Request error'))\n    })\n\n    req.on('aborted', function () {\n      handleRequestFailure(new Error('Request aborted'))\n    })\n\n    req.on('close', function () {\n      if (req.readableEnded) return\n      handleRequestFailure(new Error('Request closed'))\n    })\n\n    try {\n      busboy = Busboy({\n        headers: req.headers,\n        limits: limits,\n        preservePath: preservePath,\n        defParamCharset: defParamCharset\n      })\n    } catch (err) {\n      return next(err)\n    }\n\n    appender = new FileAppender(fileStrategy, req)\n\n    // handle text field data\n    busboy.on('field', function (fieldname, value, { nameTruncated, valueTruncated }) {\n      if (fieldname == null) return abortWithCode('MISSING_FIELD_NAME')\n      if (nameTruncated) return abortWithCode('LIMIT_FIELD_KEY')\n      if (valueTruncated) return abortWithCode('LIMIT_FIELD_VALUE', fieldname)\n\n      // Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)\n      if (limits && Object.prototype.hasOwnProperty.call(limits, 'fieldNameSize')) {\n        if (fieldname.length > limits.fieldNameSize) return abortWithCode('LIMIT_FIELD_KEY')\n      }\n\n      appendField(req.body, fieldname, value)\n    })\n\n    // handle files\n    busboy.on('file', function (fieldname, fileStream, { filename, encoding, mimeType }) {\n      var pendingWritesIncremented = false\n\n      fileStream.on('error', function (err) {\n        if (pendingWritesIncremented) {\n          pendingWrites.decrement()\n        }\n        abortWithError(err)\n      })\n\n      if (fieldname == null) return abortWithCode('MISSING_FIELD_NAME')\n\n      // don't attach to the files object, if there is no file\n      if (!filename) return fileStream.resume()\n\n      // Work around bug in Busboy (https://github.com/mscdex/busboy/issues/6)\n      if (limits && Object.prototype.hasOwnProperty.call(limits, 'fieldNameSize')) {\n        if (fieldname.length > limits.fieldNameSize) return abortWithCode('LIMIT_FIELD_KEY')\n      }\n\n      var file = {\n        fieldname: fieldname,\n        originalname: filename,\n        encoding: encoding,\n        mimetype: mimeType\n      }\n\n      var placeholder = appender.insertPlaceholder(file)\n\n      fileFilter(req, file, function (err, includeFile) {\n        if (errorOccured) {\n          appender.removePlaceholder(placeholder)\n          return fileStream.resume()\n        }\n\n        if (err) {\n          appender.removePlaceholder(placeholder)\n          return abortWithError(err)\n        }\n\n        if (!includeFile) {\n          appender.removePlaceholder(placeholder)\n          return fileStream.resume()\n        }\n\n        var aborting = false\n        pendingWritesIncremented = true\n        pendingWrites.increment()\n\n        Object.defineProperty(file, 'stream', {\n          configurable: true,\n          enumerable: false,\n          value: fileStream\n        })\n\n        fileStream.on('limit', function () {\n          aborting = true\n          abortWithCode('LIMIT_FILE_SIZE', fieldname)\n        })\n\n        storage._handleFile(req, file, function (err, info) {\n          if (aborting) {\n            appender.removePlaceholder(placeholder)\n            uploadedFiles.push({ ...file, ...info })\n            return pendingWrites.decrement()\n          }\n\n          if (err) {\n            appender.removePlaceholder(placeholder)\n            pendingWrites.decrement()\n            return abortWithError(err)\n          }\n\n          var fileInfo = { ...file, ...info }\n\n          appender.replacePlaceholder(placeholder, fileInfo)\n          uploadedFiles.push(fileInfo)\n          pendingWrites.decrement()\n          indicateDone()\n        })\n      })\n    })\n\n    busboy.on('error', function (err) { abortWithError(err) })\n    busboy.on('partsLimit', function () { abortWithCode('LIMIT_PART_COUNT') })\n    busboy.on('filesLimit', function () { abortWithCode('LIMIT_FILE_COUNT') })\n    busboy.on('fieldsLimit', function () { abortWithCode('LIMIT_FIELD_COUNT') })\n    busboy.on('close', function () {\n      readFinished = true\n      indicateDone()\n    })\n\n    req.pipe(busboy)\n  }\n}\n\nmodule.exports = makeMiddleware\n"
  },
  {
    "path": "lib/multer-error.js",
    "content": "var util = require('util')\n\nvar errorMessages = {\n  LIMIT_PART_COUNT: 'Too many parts',\n  LIMIT_FILE_SIZE: 'File too large',\n  LIMIT_FILE_COUNT: 'Too many files',\n  LIMIT_FIELD_KEY: 'Field name too long',\n  LIMIT_FIELD_VALUE: 'Field value too long',\n  LIMIT_FIELD_COUNT: 'Too many fields',\n  LIMIT_UNEXPECTED_FILE: 'Unexpected field',\n  MISSING_FIELD_NAME: 'Field name missing'\n}\n\nfunction MulterError (code, field) {\n  Error.captureStackTrace(this, this.constructor)\n  this.name = this.constructor.name\n  this.message = errorMessages[code]\n  this.code = code\n  if (field) this.field = field\n}\n\nutil.inherits(MulterError, Error)\n\nmodule.exports = MulterError\n"
  },
  {
    "path": "lib/remove-uploaded-files.js",
    "content": "function removeUploadedFiles (uploadedFiles, remove, cb) {\n  var length = uploadedFiles.length\n  var errors = []\n\n  if (length === 0) return cb(null, errors)\n\n  function handleFile (idx) {\n    var file = uploadedFiles[idx]\n\n    remove(file, function (err) {\n      if (err) {\n        err.file = file\n        err.field = file.fieldname\n        errors.push(err)\n      }\n\n      if (idx < length - 1) {\n        setImmediate(function () { handleFile(idx + 1) })\n      } else {\n        cb(null, errors)\n      }\n    })\n  }\n\n  handleFile(0)\n}\n\nmodule.exports = removeUploadedFiles\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"multer\",\n  \"description\": \"Middleware for handling `multipart/form-data`.\",\n  \"version\": \"2.1.1\",\n  \"contributors\": [\n    \"Hage Yaapa <captain@hacksparrow.com> (http://www.hacksparrow.com)\",\n    \"Jaret Pfluger <https://github.com/jpfluger>\",\n    \"Linus Unnebäck <linus@folkdatorn.se>\"\n  ],\n  \"license\": \"MIT\",\n  \"repository\": \"expressjs/multer\",\n  \"funding\": {\n    \"type\": \"opencollective\",\n    \"url\": \"https://opencollective.com/express\"\n  },\n  \"keywords\": [\n    \"form\",\n    \"post\",\n    \"multipart\",\n    \"form-data\",\n    \"formdata\",\n    \"express\",\n    \"middleware\"\n  ],\n  \"dependencies\": {\n    \"append-field\": \"^1.0.0\",\n    \"busboy\": \"^1.6.0\",\n    \"concat-stream\": \"^2.0.0\",\n    \"type-is\": \"^1.6.18\"\n  },\n  \"devDependencies\": {\n    \"deep-equal\": \"^2.0.3\",\n    \"express\": \"^4.21.2\",\n    \"form-data\": \"^4.0.2\",\n    \"fs-temp\": \"^1.2.1\",\n    \"mocha\": \"^11.5.0\",\n    \"nyc\": \"^15.1.0\",\n    \"rimraf\": \"^2.4.1\",\n    \"standard\": \"^14.3.3\",\n    \"testdata-w3c-json-form\": \"^1.0.0\"\n  },\n  \"engines\": {\n    \"node\": \">= 10.16.0\"\n  },\n  \"files\": [\n    \"LICENSE\",\n    \"index.js\",\n    \"storage/\",\n    \"lib/\"\n  ],\n  \"scripts\": {\n    \"lint\": \"standard\",\n    \"lint:fix\": \"standard --fix\",\n    \"test\": \"mocha --reporter spec --exit --check-leaks test/\",\n    \"test-ci\": \"nyc --reporter=lcov --reporter=text npm test\",\n    \"test-cov\": \"nyc --reporter=html --reporter=text npm test\"\n  }\n}\n"
  },
  {
    "path": "storage/disk.js",
    "content": "var fs = require('fs')\nvar os = require('os')\nvar path = require('path')\nvar crypto = require('crypto')\n\nfunction getFilename (req, file, cb) {\n  crypto.randomBytes(16, function (err, raw) {\n    cb(err, err ? undefined : raw.toString('hex'))\n  })\n}\n\nfunction getDestination (req, file, cb) {\n  cb(null, os.tmpdir())\n}\n\nfunction DiskStorage (opts) {\n  this.getFilename = (opts.filename || getFilename)\n\n  if (typeof opts.destination === 'string') {\n    fs.mkdirSync(opts.destination, { recursive: true })\n    this.getDestination = function ($0, $1, cb) { cb(null, opts.destination) }\n  } else {\n    this.getDestination = (opts.destination || getDestination)\n  }\n}\n\nDiskStorage.prototype._handleFile = function _handleFile (req, file, cb) {\n  var that = this\n\n  that.getDestination(req, file, function (err, destination) {\n    if (err) return cb(err)\n\n    that.getFilename(req, file, function (err, filename) {\n      if (err) return cb(err)\n\n      var finalPath = path.join(destination, filename)\n      var outStream = fs.createWriteStream(finalPath)\n\n      file.stream.pipe(outStream)\n      outStream.on('error', cb)\n      outStream.on('finish', function () {\n        cb(null, {\n          destination: destination,\n          filename: filename,\n          path: finalPath,\n          size: outStream.bytesWritten\n        })\n      })\n    })\n  })\n}\n\nDiskStorage.prototype._removeFile = function _removeFile (req, file, cb) {\n  var path = file.path\n\n  delete file.destination\n  delete file.filename\n  delete file.path\n\n  fs.unlink(path, cb)\n}\n\nmodule.exports = function (opts) {\n  return new DiskStorage(opts)\n}\n"
  },
  {
    "path": "storage/memory.js",
    "content": "var concat = require('concat-stream')\n\nfunction MemoryStorage (opts) {}\n\nMemoryStorage.prototype._handleFile = function _handleFile (req, file, cb) {\n  file.stream.pipe(concat({ encoding: 'buffer' }, function (data) {\n    cb(null, {\n      buffer: data,\n      size: data.length\n    })\n  }))\n}\n\nMemoryStorage.prototype._removeFile = function _removeFile (req, file, cb) {\n  delete file.buffer\n  cb(null)\n}\n\nmodule.exports = function (opts) {\n  return new MemoryStorage(opts)\n}\n"
  },
  {
    "path": "test/_util.js",
    "content": "var fs = require('fs')\nvar path = require('path')\nvar stream = require('stream')\n\nexports.file = function file (name) {\n  return fs.createReadStream(path.join(__dirname, 'files', name))\n}\n\nexports.fileSize = function fileSize (path) {\n  return fs.statSync(path).size\n}\n\nexports.submitForm = function submitForm (multer, form, cb) {\n  form.getLength(function (err, length) {\n    if (err) return cb(err)\n\n    var req = new stream.PassThrough()\n\n    form.pipe(req)\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + form.getBoundary(),\n      'content-length': length\n    }\n\n    multer(req, null, function (err) {\n      cb(err, req)\n    })\n  })\n}\n"
  },
  {
    "path": "test/async-file-filter-orphan.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\nvar fs = require('fs')\nvar os = require('os')\nvar path = require('path')\nvar http = require('http')\n\nvar express = require('express')\nvar multer = require('../')\n\ndescribe('async fileFilter cleanup', function () {\n  it('does not leave orphan files when request aborts with missing field name', function (done) {\n    var uploadDir = fs.mkdtempSync(path.join(os.tmpdir(), 'multer-orphan-'))\n    var app = express()\n\n    var upload = multer({\n      dest: uploadDir,\n      fileFilter: function (req, file, cb) {\n        setImmediate(function () { cb(null, true) })\n      }\n    })\n\n    app.post('/upload', upload.any(), function (req, res) {\n      res.json({ success: true })\n    })\n\n    app.use(function (err, req, res, next) {\n      res.status(400).json({ error: err.code })\n    })\n\n    var server = app.listen(0, function () {\n      var port = server.address().port\n      var boundary = 'TestBound'\n      var body =\n        '--' + boundary + '\\r\\n' +\n        'Content-Disposition: form-data; name=\"f\"; filename=\"a.bin\"\\r\\n' +\n        'Content-Type: application/octet-stream\\r\\n\\r\\nORPHAN FILE DATA\\r\\n' +\n        '--' + boundary + '\\r\\n' +\n        'Content-Disposition: form-data; filename=\"b.bin\"\\r\\n' +\n        'Content-Type: application/octet-stream\\r\\n\\r\\nx\\r\\n' +\n        '--' + boundary + '--\\r\\n'\n\n      var req = http.request({\n        hostname: 'localhost',\n        port: port,\n        path: '/upload',\n        method: 'POST',\n        headers: {\n          'Content-Type': 'multipart/form-data; boundary=' + boundary,\n          'Content-Length': Buffer.byteLength(body)\n        }\n      }, function (res) {\n        res.resume()\n        res.on('end', function () {\n          setTimeout(function () {\n            var files = fs.readdirSync(uploadDir)\n            assert.strictEqual(res.statusCode, 400)\n            assert.strictEqual(files.length, 0)\n            server.close(done)\n          }, 500)\n        })\n      })\n\n      req.on('error', function (err) {\n        server.close(function () {\n          done(err)\n        })\n      })\n\n      req.write(body)\n      req.end()\n    })\n  })\n}\n)\n"
  },
  {
    "path": "test/def-param-charset.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\nvar stream = require('stream')\n\nvar multer = require('../')\nvar temp = require('fs-temp')\nvar rimraf = require('rimraf')\n\ndescribe('defParamCharset', function () {\n  var uploadDir, upload\n\n  beforeEach(function (done) {\n    temp.mkdir(function (err, path) {\n      if (err) return done(err)\n\n      var storage = multer.diskStorage({\n        destination: path,\n        filename: function (req, file, cb) {\n          cb(null, file.originalname)\n        }\n      })\n\n      uploadDir = path\n      upload = multer({ storage: storage })\n      done()\n    })\n  })\n\n  afterEach(function (done) {\n    rimraf(uploadDir, done)\n  })\n\n  it('should use latin1 as default charset for non-extended parameters', function (done) {\n    var req = new stream.PassThrough()\n    var boundary = 'AaB03x'\n\n    // Create a filename with latin1 characters (e.g., café encoded as latin1)\n    // In latin1: é = 0xE9\n    var bodyParts = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"testfile\"; filename=\"caf',\n      '.txt\"',\n      'Content-Type: text/plain',\n      '',\n      'test file content',\n      '--' + boundary + '--'\n    ]\n\n    // Create buffer with proper latin1 encoding\n    var bodyBuffer = Buffer.concat([\n      Buffer.from(bodyParts[0] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[1], 'ascii'),\n      Buffer.from([0xE9]), // é in latin1\n      Buffer.from(bodyParts[2] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[3] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[4] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[5] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[6], 'ascii')\n    ])\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': bodyBuffer.length\n    }\n\n    req.end(bodyBuffer)\n\n    upload.single('testfile')(req, null, function (err) {\n      assert.ifError(err)\n\n      // With latin1 (default), the filename should be interpreted as latin1\n      assert.strictEqual(req.file.originalname, 'café.txt')\n      assert.strictEqual(req.file.fieldname, 'testfile')\n\n      done()\n    })\n  })\n\n  it('should use custom charset when defParamCharset is specified', function (done) {\n    var customUpload = multer({\n      storage: multer.diskStorage({\n        destination: uploadDir,\n        filename: function (req, file, cb) {\n          cb(null, file.originalname)\n        }\n      }),\n      defParamCharset: 'utf8'\n    })\n\n    var req = new stream.PassThrough()\n    var boundary = 'AaB03x'\n\n    // Create a filename with UTF-8 characters (e.g., café encoded as UTF-8)\n    // In UTF-8: é = 0xC3 0xA9\n    var bodyParts = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"testfile\"; filename=\"caf',\n      '.txt\"',\n      'Content-Type: text/plain',\n      '',\n      'test file content',\n      '--' + boundary + '--'\n    ]\n\n    // Create buffer with proper UTF-8 encoding\n    var bodyBuffer = Buffer.concat([\n      Buffer.from(bodyParts[0] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[1], 'ascii'),\n      Buffer.from([0xC3, 0xA9]), // é in UTF-8\n      Buffer.from(bodyParts[2] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[3] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[4] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[5] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[6], 'ascii')\n    ])\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': bodyBuffer.length\n    }\n\n    req.end(bodyBuffer)\n\n    customUpload.single('testfile')(req, null, function (err) {\n      assert.ifError(err)\n\n      // With utf8, the filename should be interpreted as utf8\n      assert.strictEqual(req.file.originalname, 'café.txt')\n      assert.strictEqual(req.file.fieldname, 'testfile')\n\n      done()\n    })\n  })\n\n  it('should not affect extended parameters with explicit charset', function (done) {\n    var customUpload = multer({\n      storage: multer.diskStorage({\n        destination: uploadDir,\n        filename: function (req, file, cb) {\n          cb(null, file.originalname)\n        }\n      }),\n      defParamCharset: 'ascii' // This should be ignored for extended parameters\n    })\n\n    var req = new stream.PassThrough()\n    var boundary = 'AaB03x'\n\n    var body = [\n      '--' + boundary,\n      // Extended parameter with explicit UTF-8 charset should override defParamCharset\n      'Content-Disposition: form-data; name=\"testfile\"; filename*=utf-8\\'\\'caf%C3%A9.txt',\n      'Content-Type: text/plain',\n      '',\n      'test file content',\n      '--' + boundary + '--'\n    ].join('\\r\\n')\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': body.length\n    }\n\n    req.end(body)\n\n    customUpload.single('testfile')(req, null, function (err) {\n      assert.ifError(err)\n\n      // Extended parameter should use its own charset (UTF-8), not defParamCharset\n      assert.strictEqual(req.file.originalname, 'café.txt')\n      assert.strictEqual(req.file.fieldname, 'testfile')\n\n      done()\n    })\n  })\n\n  it('should handle different charsets correctly', function (done) {\n    var customUpload = multer({\n      storage: multer.diskStorage({\n        destination: uploadDir,\n        filename: function (req, file, cb) {\n          cb(null, file.originalname)\n        }\n      }),\n      defParamCharset: 'iso-8859-1' // Same as latin1\n    })\n\n    var req = new stream.PassThrough()\n    var boundary = 'AaB03x'\n\n    // Test with ISO-8859-1 encoded characters\n    var bodyParts = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"testfile\"; filename=\"test',\n      '.txt\"',\n      'Content-Type: text/plain',\n      '',\n      'test file content',\n      '--' + boundary + '--'\n    ]\n\n    // Create buffer with proper ISO-8859-1 encoding\n    var bodyBuffer = Buffer.concat([\n      Buffer.from(bodyParts[0] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[1], 'ascii'),\n      Buffer.from([0xF1]), // ñ in ISO-8859-1\n      Buffer.from(bodyParts[2] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[3] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[4] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[5] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[6], 'ascii')\n    ])\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': bodyBuffer.length\n    }\n\n    req.end(bodyBuffer)\n\n    customUpload.single('testfile')(req, null, function (err) {\n      assert.ifError(err)\n\n      // Should correctly decode the ñ character\n      assert.strictEqual(req.file.originalname, 'testñ.txt')\n      assert.strictEqual(req.file.fieldname, 'testfile')\n\n      done()\n    })\n  })\n\n  it('should work with memory storage', function (done) {\n    var memoryUpload = multer({\n      storage: multer.memoryStorage(),\n      defParamCharset: 'utf8'\n    })\n\n    var req = new stream.PassThrough()\n    var boundary = 'AaB03x'\n\n    var bodyParts = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"testfile\"; filename=\"test',\n      '.txt\"',\n      'Content-Type: text/plain',\n      '',\n      'test file content',\n      '--' + boundary + '--'\n    ]\n\n    // Create buffer with proper UTF-8 encoding\n    var bodyBuffer = Buffer.concat([\n      Buffer.from(bodyParts[0] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[1], 'ascii'),\n      Buffer.from([0xC3, 0xA9]), // é in UTF-8\n      Buffer.from(bodyParts[2] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[3] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[4] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[5] + '\\r\\n', 'ascii'),\n      Buffer.from(bodyParts[6], 'ascii')\n    ])\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': bodyBuffer.length\n    }\n\n    req.end(bodyBuffer)\n\n    memoryUpload.single('testfile')(req, null, function (err) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.file.originalname, 'testé.txt')\n      assert.strictEqual(req.file.fieldname, 'testfile')\n      assert.ok(Buffer.isBuffer(req.file.buffer))\n\n      done()\n    })\n  })\n\n  it('should work with array uploads', function (done) {\n    var customUpload = multer({\n      storage: multer.diskStorage({\n        destination: uploadDir,\n        filename: function (req, file, cb) {\n          cb(null, file.originalname)\n        }\n      }),\n      defParamCharset: 'utf8'\n    })\n\n    var req = new stream.PassThrough()\n    var boundary = 'AaB03x'\n\n    // Create buffer with proper UTF-8 encoding for both files\n    var bodyBuffer = Buffer.concat([\n      Buffer.from('--' + boundary + '\\r\\n', 'ascii'),\n      Buffer.from('Content-Disposition: form-data; name=\"testfiles\"; filename=\"file1', 'ascii'),\n      Buffer.from([0xC3, 0xA9]), // é in UTF-8\n      Buffer.from('.txt\"\\r\\n', 'ascii'),\n      Buffer.from('Content-Type: text/plain\\r\\n', 'ascii'),\n      Buffer.from('\\r\\n', 'ascii'),\n      Buffer.from('test file 1 content\\r\\n', 'ascii'),\n      Buffer.from('--' + boundary + '\\r\\n', 'ascii'),\n      Buffer.from('Content-Disposition: form-data; name=\"testfiles\"; filename=\"file2', 'ascii'),\n      Buffer.from([0xC3, 0xB1]), // ñ in UTF-8\n      Buffer.from('.txt\"\\r\\n', 'ascii'),\n      Buffer.from('Content-Type: text/plain\\r\\n', 'ascii'),\n      Buffer.from('\\r\\n', 'ascii'),\n      Buffer.from('test file 2 content\\r\\n', 'ascii'),\n      Buffer.from('--' + boundary + '--', 'ascii')\n    ])\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': bodyBuffer.length\n    }\n\n    req.end(bodyBuffer)\n\n    customUpload.array('testfiles', 2)(req, null, function (err) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.files.length, 2)\n      assert.strictEqual(req.files[0].originalname, 'file1é.txt')\n      assert.strictEqual(req.files[1].originalname, 'file2ñ.txt')\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/disk-storage.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\nvar deepEqual = require('deep-equal')\n\nvar fs = require('fs')\nvar path = require('path')\nvar util = require('./_util')\nvar multer = require('../')\nvar temp = require('fs-temp')\nvar rimraf = require('rimraf')\nvar FormData = require('form-data')\n\ndescribe('Disk Storage', function () {\n  var uploadDir, upload\n\n  beforeEach(function (done) {\n    temp.mkdir(function (err, path) {\n      if (err) return done(err)\n\n      uploadDir = path\n      upload = multer({ dest: path })\n      done()\n    })\n  })\n\n  afterEach(function (done) {\n    rimraf(uploadDir, done)\n  })\n\n  it('should process parser/form-data POST request', function (done) {\n    var form = new FormData()\n    var parser = upload.single('small0')\n\n    form.append('name', 'Multer')\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.body.name, 'Multer')\n\n      assert.strictEqual(req.file.fieldname, 'small0')\n      assert.strictEqual(req.file.originalname, 'small0.dat')\n      assert.strictEqual(req.file.size, 1778)\n      assert.strictEqual(util.fileSize(req.file.path), 1778)\n\n      done()\n    })\n  })\n\n  it('should process empty fields and an empty file', function (done) {\n    var form = new FormData()\n    var parser = upload.single('empty')\n\n    form.append('empty', util.file('empty.dat'))\n    form.append('name', 'Multer')\n    form.append('version', '')\n    form.append('year', '')\n    form.append('checkboxfull', 'cb1')\n    form.append('checkboxfull', 'cb2')\n    form.append('checkboxhalfempty', 'cb1')\n    form.append('checkboxhalfempty', '')\n    form.append('checkboxempty', '')\n    form.append('checkboxempty', '')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.body.name, 'Multer')\n      assert.strictEqual(req.body.version, '')\n      assert.strictEqual(req.body.year, '')\n\n      assert(deepEqual(req.body.checkboxfull, ['cb1', 'cb2']))\n      assert(deepEqual(req.body.checkboxhalfempty, ['cb1', '']))\n      assert(deepEqual(req.body.checkboxempty, ['', '']))\n\n      assert.strictEqual(req.file.fieldname, 'empty')\n      assert.strictEqual(req.file.originalname, 'empty.dat')\n      assert.strictEqual(req.file.size, 0)\n      assert.strictEqual(util.fileSize(req.file.path), 0)\n\n      done()\n    })\n  })\n\n  it('should process multiple files', function (done) {\n    var form = new FormData()\n    var parser = upload.fields([\n      { name: 'empty', maxCount: 1 },\n      { name: 'tiny0', maxCount: 1 },\n      { name: 'tiny1', maxCount: 1 },\n      { name: 'small0', maxCount: 1 },\n      { name: 'small1', maxCount: 1 },\n      { name: 'medium', maxCount: 1 },\n      { name: 'large', maxCount: 1 }\n    ])\n\n    form.append('empty', util.file('empty.dat'))\n    form.append('tiny0', util.file('tiny0.dat'))\n    form.append('tiny1', util.file('tiny1.dat'))\n    form.append('small0', util.file('small0.dat'))\n    form.append('small1', util.file('small1.dat'))\n    form.append('medium', util.file('medium.dat'))\n    form.append('large', util.file('large.jpg'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n\n      assert(deepEqual(req.body, {}))\n\n      assert.strictEqual(req.files.empty[0].fieldname, 'empty')\n      assert.strictEqual(req.files.empty[0].originalname, 'empty.dat')\n      assert.strictEqual(req.files.empty[0].size, 0)\n      assert.strictEqual(util.fileSize(req.files.empty[0].path), 0)\n\n      assert.strictEqual(req.files.tiny0[0].fieldname, 'tiny0')\n      assert.strictEqual(req.files.tiny0[0].originalname, 'tiny0.dat')\n      assert.strictEqual(req.files.tiny0[0].size, 122)\n      assert.strictEqual(util.fileSize(req.files.tiny0[0].path), 122)\n\n      assert.strictEqual(req.files.tiny1[0].fieldname, 'tiny1')\n      assert.strictEqual(req.files.tiny1[0].originalname, 'tiny1.dat')\n      assert.strictEqual(req.files.tiny1[0].size, 7)\n      assert.strictEqual(util.fileSize(req.files.tiny1[0].path), 7)\n\n      assert.strictEqual(req.files.small0[0].fieldname, 'small0')\n      assert.strictEqual(req.files.small0[0].originalname, 'small0.dat')\n      assert.strictEqual(req.files.small0[0].size, 1778)\n      assert.strictEqual(util.fileSize(req.files.small0[0].path), 1778)\n\n      assert.strictEqual(req.files.small1[0].fieldname, 'small1')\n      assert.strictEqual(req.files.small1[0].originalname, 'small1.dat')\n      assert.strictEqual(req.files.small1[0].size, 315)\n      assert.strictEqual(util.fileSize(req.files.small1[0].path), 315)\n\n      assert.strictEqual(req.files.medium[0].fieldname, 'medium')\n      assert.strictEqual(req.files.medium[0].originalname, 'medium.dat')\n      assert.strictEqual(req.files.medium[0].size, 13196)\n      assert.strictEqual(util.fileSize(req.files.medium[0].path), 13196)\n\n      assert.strictEqual(req.files.large[0].fieldname, 'large')\n      assert.strictEqual(req.files.large[0].originalname, 'large.jpg')\n      assert.strictEqual(req.files.large[0].size, 2413677)\n      assert.strictEqual(util.fileSize(req.files.large[0].path), 2413677)\n\n      done()\n    })\n  })\n\n  it('should remove uploaded files on error', function (done) {\n    var form = new FormData()\n    var parser = upload.single('tiny0')\n\n    form.append('tiny0', util.file('tiny0.dat'))\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(err.field, 'small0')\n      assert(deepEqual(err.storageErrors, []))\n\n      var files = fs.readdirSync(uploadDir)\n      assert(deepEqual(files, []))\n\n      done()\n    })\n  })\n\n  it('should report error when directory doesn\\'t exist', function (done) {\n    var directory = path.join(temp.mkdirSync(), 'ghost')\n    function dest ($0, $1, cb) { cb(null, directory) }\n\n    var storage = multer.diskStorage({ destination: dest })\n    var upload = multer({ storage: storage })\n    var parser = upload.single('tiny0')\n    var form = new FormData()\n\n    form.append('tiny0', util.file('tiny0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'ENOENT')\n      assert.strictEqual(path.dirname(err.path), directory)\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/error-handling.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar os = require('os')\nvar util = require('./_util')\nvar multer = require('../')\nvar removeUploadedFiles = require('../lib/remove-uploaded-files')\nvar stream = require('stream')\nvar FormData = require('form-data')\nvar http = require('http')\nvar net = require('net')\n\nfunction withLimits (limits, fields) {\n  var storage = multer.memoryStorage()\n  return multer({ storage: storage, limits: limits }).fields(fields)\n}\n\ndescribe('Error Handling', function () {\n  it('should be an instance of both `Error` and `MulterError` classes in case of the Multer\\'s error', function (done) {\n    var form = new FormData()\n    var storage = multer.diskStorage({ destination: os.tmpdir() })\n    var upload = multer({ storage: storage }).fields([\n      { name: 'small0', maxCount: 1 }\n    ])\n\n    form.append('small0', util.file('small0.dat'))\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(upload, form, function (err, req) {\n      assert.strictEqual(err instanceof Error, true)\n      assert.strictEqual(err instanceof multer.MulterError, true)\n      done()\n    })\n  })\n\n  it('should respect parts limit', function (done) {\n    var form = new FormData()\n    var parser = withLimits({ parts: 1 }, [\n      { name: 'small0', maxCount: 1 }\n    ])\n\n    form.append('field0', 'BOOM!')\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_PART_COUNT')\n      done()\n    })\n  })\n\n  it('should respect file size limit', function (done) {\n    var form = new FormData()\n    var parser = withLimits({ fileSize: 1500 }, [\n      { name: 'tiny0', maxCount: 1 },\n      { name: 'small0', maxCount: 1 }\n    ])\n\n    form.append('tiny0', util.file('tiny0.dat'))\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_FILE_SIZE')\n      assert.strictEqual(err.field, 'small0')\n      done()\n    })\n  })\n\n  it('should respect file count limit', function (done) {\n    var form = new FormData()\n    var parser = withLimits({ files: 1 }, [\n      { name: 'small0', maxCount: 1 },\n      { name: 'small1', maxCount: 1 }\n    ])\n\n    form.append('small0', util.file('small0.dat'))\n    form.append('small1', util.file('small1.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_FILE_COUNT')\n      done()\n    })\n  })\n\n  it('should respect file key limit', function (done) {\n    var form = new FormData()\n    var parser = withLimits({ fieldNameSize: 4 }, [\n      { name: 'small0', maxCount: 1 }\n    ])\n\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_FIELD_KEY')\n      done()\n    })\n  })\n\n  it('should respect field key limit', function (done) {\n    var form = new FormData()\n    var parser = withLimits({ fieldNameSize: 4 }, [])\n\n    form.append('ok', 'SMILE')\n    form.append('blowup', 'BOOM!')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_FIELD_KEY')\n      done()\n    })\n  })\n\n  it('should respect field value limit', function (done) {\n    var form = new FormData()\n    var parser = withLimits({ fieldSize: 16 }, [])\n\n    form.append('field0', 'This is okay')\n    form.append('field1', 'This will make the parser explode')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_FIELD_VALUE')\n      assert.strictEqual(err.field, 'field1')\n      done()\n    })\n  })\n\n  it('should respect field count limit', function (done) {\n    var form = new FormData()\n    var parser = withLimits({ fields: 1 }, [])\n\n    form.append('field0', 'BOOM!')\n    form.append('field1', 'BOOM!')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_FIELD_COUNT')\n      done()\n    })\n  })\n\n  it('should respect fields given', function (done) {\n    var form = new FormData()\n    var parser = withLimits(undefined, [\n      { name: 'wrongname', maxCount: 1 }\n    ])\n\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(err.field, 'small0')\n      done()\n    })\n  })\n\n  it('should notify of missing field name', function (done) {\n    var req = new stream.PassThrough()\n    var storage = multer.memoryStorage()\n    var upload = multer({ storage: storage }).single('tiny0')\n    var boundary = 'AaB03x'\n    var body = [\n      '--' + boundary,\n      'Content-Disposition: form-data',\n      '',\n      'test content',\n      '--' + boundary,\n      ''\n    ].join('\\r\\n')\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': body.length\n    }\n\n    req.end(body)\n\n    upload(req, null, function (err) {\n      assert.strictEqual(err.code, 'MISSING_FIELD_NAME')\n      done()\n    })\n  })\n\n  it('should notify of missing field name', function (done) {\n    var form = new FormData()\n    var storage = multer.memoryStorage()\n    var parser = multer({ storage: storage }).single('small0')\n\n    form.append('', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'MISSING_FIELD_NAME')\n      done()\n    })\n  })\n\n  it('should report errors from storage engines', function (done) {\n    var storage = multer.memoryStorage()\n\n    storage._removeFile = function _removeFile (req, file, cb) {\n      var err = new Error('Test error')\n      err.code = 'TEST'\n      cb(err)\n    }\n\n    var form = new FormData()\n    var upload = multer({ storage: storage })\n    var parser = upload.single('tiny0')\n\n    form.append('tiny0', util.file('tiny0.dat'))\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(err.field, 'small0')\n\n      assert.strictEqual(err.storageErrors.length, 1)\n      assert.strictEqual(err.storageErrors[0].code, 'TEST')\n      assert.strictEqual(err.storageErrors[0].field, 'tiny0')\n      assert.strictEqual(err.storageErrors[0].file, req.file)\n\n      done()\n    })\n  })\n\n  it('should report errors from busboy constructor', function (done) {\n    var req = new stream.PassThrough()\n    var storage = multer.memoryStorage()\n    var upload = multer({ storage: storage }).single('tiny0')\n    var body = 'test'\n\n    req.headers = {\n      'content-type': 'multipart/form-data',\n      'content-length': body.length\n    }\n\n    req.end(body)\n\n    upload(req, null, function (err) {\n      assert.strictEqual(err.message, 'Multipart: Boundary not found')\n      done()\n    })\n  })\n\n  it('should report errors from busboy parsing', function (done) {\n    var req = new stream.PassThrough()\n    var storage = multer.memoryStorage()\n    var upload = multer({ storage: storage }).single('tiny0')\n    var boundary = 'AaB03x'\n    var body = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"tiny0\"; filename=\"test.txt\"',\n      'Content-Type: text/plain',\n      '',\n      'test without end boundary'\n    ].join('\\r\\n')\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': body.length\n    }\n\n    req.end(body)\n\n    upload(req, null, function (err) {\n      assert.strictEqual(err.message, 'Unexpected end of form')\n      done()\n    })\n  })\n\n  it('should gracefully handle more than one error at a time', function (done) {\n    var form = new FormData()\n    var storage = multer.diskStorage({ destination: os.tmpdir() })\n    var upload = multer({ storage: storage, limits: { fileSize: 1, files: 1 } }).fields([\n      { name: 'small0', maxCount: 1 }\n    ])\n\n    form.append('small0', util.file('small0.dat'))\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(upload, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_FILE_SIZE')\n      done()\n    })\n  })\n\n  it('should allow client to finish sending body before error response', function (done) {\n    this.timeout(10000)\n\n    var upload = multer({ storage: multer.memoryStorage() }).single('expected')\n\n    var server = http.createServer(function (req, res) {\n      upload(req, res, function (err) {\n        res.statusCode = err ? 500 : 200\n        res.end(err ? err.code : 'OK')\n      })\n    })\n\n    server.listen(0, function () {\n      var port = server.address().port\n      var boundary = 'Drain' + Date.now()\n      var preamble = [\n        '--' + boundary,\n        'Content-Disposition: form-data; name=\"unexpected\"; filename=\"test.bin\"',\n        'Content-Type: application/octet-stream',\n        '',\n        ''\n      ].join('\\r\\n')\n      var footer = '\\r\\n--' + boundary + '--\\r\\n'\n      var chunk = Buffer.alloc(32 * 1024, 97)\n      var totalChunks = 24\n      var contentLength = Buffer.byteLength(preamble) +\n        (chunk.length * totalChunks) +\n        Buffer.byteLength(footer)\n\n      var sock = new net.Socket()\n      var socketError = null\n      var response = ''\n      var sentChunks = 0\n      var finished = false\n      var timeout = setTimeout(function () {\n        if (finished) return\n        finished = true\n        sock.destroy()\n        server.close(function () {\n          done(new Error('timed out while uploading request body'))\n        })\n      }, 8000)\n\n      function finish (err) {\n        if (finished) return\n        finished = true\n        clearTimeout(timeout)\n        server.close(function () {\n          done(err)\n        })\n      }\n\n      function writeChunk () {\n        if (sentChunks >= totalChunks) {\n          sock.write(footer)\n          return\n        }\n\n        sentChunks += 1\n        var canContinue = sock.write(chunk)\n\n        if (canContinue) {\n          setTimeout(writeChunk, 2)\n        } else {\n          sock.once('drain', function () {\n            setTimeout(writeChunk, 2)\n          })\n        }\n      }\n\n      sock.connect(port, '127.0.0.1', function () {\n        sock.write(\n          'POST / HTTP/1.1\\r\\n' +\n          'Host: localhost\\r\\n' +\n          'Connection: close\\r\\n' +\n          'Content-Type: multipart/form-data; boundary=' + boundary + '\\r\\n' +\n          'Content-Length: ' + contentLength + '\\r\\n\\r\\n'\n        )\n        sock.write(preamble)\n        writeChunk()\n      })\n\n      sock.on('data', function (buf) {\n        response += buf.toString('utf8')\n      })\n\n      sock.on('error', function (err) {\n        socketError = err\n      })\n\n      sock.on('close', function () {\n        if (socketError) return finish(socketError)\n\n        try {\n          assert.strictEqual(sentChunks, totalChunks)\n          assert.ok(/HTTP\\/1\\.1 500/.test(response))\n          finish()\n        } catch (err) {\n          finish(err)\n        }\n      })\n    })\n  })\n\n  it('should not hang when client aborts multipart upload', function (done) {\n    this.timeout(5000)\n\n    var upload = multer({ storage: multer.memoryStorage() }).any()\n\n    var server = http.createServer(function (req, res) {\n      var hung = false\n\n      var timer = setTimeout(function () {\n        hung = true\n        server.close()\n        done(new Error('Middleware hung when client aborted request'))\n      }, 1000)\n\n      upload(req, res, function (/* err */) {\n        if (hung) return\n        clearTimeout(timer)\n        server.close()\n        done()\n      })\n    })\n\n    server.listen(0, function () {\n      var port = server.address().port\n      var boundary = 'PoC' + Date.now()\n      var sock = new net.Socket()\n\n      sock.connect(port, '127.0.0.1', function () {\n        sock.write(\n          'POST / HTTP/1.1\\r\\n' +\n          'Host: localhost\\r\\n' +\n          'Content-Type: multipart/form-data; boundary=' + boundary + '\\r\\n' +\n          'Content-Length: 999999\\r\\n\\r\\n' +\n          '--' + boundary + '\\r\\n' +\n          'Content-Disposition: form-data; name=\"file\"; filename=\"test.bin\"\\r\\n' +\n          'Content-Type: application/octet-stream\\r\\n\\r\\n' +\n          'AAAAAAAAAAAAAAAA'\n        )\n\n        setTimeout(function () {\n          sock.destroy()\n        }, 50)\n      })\n\n      sock.on('error', function () {})\n    })\n  })\n\n  it('should not overflow call stack when cleaning up many files (memory storage sync remove)', function (done) {\n    // - without setImmediate in remove-uploaded-files, synchronous _removeFile (e.g. memory storage)\n    //     causes handleFile(0) -> remove -> cb() -> handleFile(1) -> ... in one stack,\n    //     leading to \"Maximum call stack size exceeded\"\n    // - use enough files to exceed typical node stack depth (~10k - 30k)\n\n    this.timeout(10 * 1000)\n\n    var fileCount = 25000\n    var uploadedFiles = []\n\n    for (var i = 0; i < fileCount; i++) {\n      uploadedFiles.push({ fieldname: 'file', originalname: 'f.dat', buffer: Buffer.alloc(0) })\n    }\n\n    function syncRemove (file, cb) {\n      delete file.buffer\n      cb(null)\n    }\n\n    removeUploadedFiles(uploadedFiles, syncRemove, function (err, errors) {\n      assert.ifError(err)\n      assert.strictEqual(errors.length, 0)\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/expected-files.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\n\ndescribe('Expected files', function () {\n  var upload\n\n  before(function (done) {\n    upload = multer()\n    done()\n  })\n\n  it('should reject single unexpected file', function (done) {\n    var form = new FormData()\n    var parser = upload.single('butme')\n\n    form.append('notme', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(err.field, 'notme')\n      done()\n    })\n  })\n\n  it('should reject array of multiple files', function (done) {\n    var form = new FormData()\n    var parser = upload.array('butme', 4)\n\n    form.append('notme', util.file('small0.dat'))\n    form.append('notme', util.file('small1.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(err.field, 'notme')\n      done()\n    })\n  })\n\n  it('should reject overflowing arrays', function (done) {\n    var form = new FormData()\n    var parser = upload.array('butme', 1)\n\n    form.append('butme', util.file('small0.dat'))\n    form.append('butme', util.file('small1.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(err.field, 'butme')\n      done()\n    })\n  })\n\n  it('should accept files with expected fieldname', function (done) {\n    var form = new FormData()\n    var parser = upload.fields([\n      { name: 'butme', maxCount: 2 },\n      { name: 'andme', maxCount: 2 }\n    ])\n\n    form.append('butme', util.file('small0.dat'))\n    form.append('butme', util.file('small1.dat'))\n    form.append('andme', util.file('empty.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.files.butme.length, 2)\n      assert.strictEqual(req.files.andme.length, 1)\n\n      done()\n    })\n  })\n\n  it('should reject files with unexpected fieldname', function (done) {\n    var form = new FormData()\n    var parser = upload.fields([\n      { name: 'butme', maxCount: 2 },\n      { name: 'andme', maxCount: 2 }\n    ])\n\n    form.append('butme', util.file('small0.dat'))\n    form.append('butme', util.file('small1.dat'))\n    form.append('andme', util.file('empty.dat'))\n    form.append('notme', util.file('empty.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(err.field, 'notme')\n      done()\n    })\n  })\n\n  it('should allow any file to come thru', function (done) {\n    var form = new FormData()\n    var parser = upload.any()\n\n    form.append('butme', util.file('small0.dat'))\n    form.append('butme', util.file('small1.dat'))\n    form.append('andme', util.file('empty.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert.strictEqual(req.files.length, 3)\n      assert.strictEqual(req.files[0].fieldname, 'butme')\n      assert.strictEqual(req.files[1].fieldname, 'butme')\n      assert.strictEqual(req.files[2].fieldname, 'andme')\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/express-integration.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\nvar http = require('http')\n\nvar multer = require('../')\nvar util = require('./_util')\n\nvar express = require('express')\nvar FormData = require('form-data')\nvar concat = require('concat-stream')\n\nvar port = 34279\n\ndescribe('Express Integration', function () {\n  var app\n\n  before(function (done) {\n    app = express()\n    app.listen(port, done)\n  })\n\n  function submitForm (form, path, cb) {\n    var req = form.submit('http://localhost:' + port + path)\n\n    req.on('error', cb)\n    req.on('response', function (res) {\n      res.on('error', cb)\n      res.pipe(concat({ encoding: 'buffer' }, function (body) {\n        cb(null, res, body)\n      }))\n    })\n  }\n\n  it('should work with express error handling', function (done) {\n    var limits = { fileSize: 200 }\n    var upload = multer({ limits: limits })\n    var router = new express.Router()\n    var form = new FormData()\n\n    var routeCalled = 0\n    var errorCalled = 0\n\n    form.append('avatar', util.file('large.jpg'))\n\n    router.post('/profile', upload.single('avatar'), function (req, res, next) {\n      routeCalled++\n      res.status(200).end('SUCCESS')\n    })\n\n    router.use(function (err, req, res, next) {\n      assert.strictEqual(err.code, 'LIMIT_FILE_SIZE')\n\n      errorCalled++\n      res.status(500).end('ERROR')\n    })\n\n    app.use('/t1', router)\n    submitForm(form, '/t1/profile', function (err, res, body) {\n      assert.ifError(err)\n\n      assert.strictEqual(routeCalled, 0)\n      assert.strictEqual(errorCalled, 1)\n      assert.strictEqual(body.toString(), 'ERROR')\n      assert.strictEqual(res.statusCode, 500)\n\n      done()\n    })\n  })\n\n  it('should work when receiving error from fileFilter', function (done) {\n    function fileFilter (req, file, cb) {\n      cb(new Error('TEST'))\n    }\n\n    var upload = multer({ fileFilter: fileFilter })\n    var router = new express.Router()\n    var form = new FormData()\n\n    var routeCalled = 0\n    var errorCalled = 0\n\n    form.append('avatar', util.file('large.jpg'))\n\n    router.post('/profile', upload.single('avatar'), function (req, res, next) {\n      routeCalled++\n      res.status(200).end('SUCCESS')\n    })\n\n    router.use(function (err, req, res, next) {\n      assert.strictEqual(err.message, 'TEST')\n\n      errorCalled++\n      res.status(500).end('ERROR')\n    })\n\n    app.use('/t2', router)\n    submitForm(form, '/t2/profile', function (err, res, body) {\n      assert.ifError(err)\n\n      assert.strictEqual(routeCalled, 0)\n      assert.strictEqual(errorCalled, 1)\n      assert.strictEqual(body.toString(), 'ERROR')\n      assert.strictEqual(res.statusCode, 500)\n\n      done()\n    })\n  })\n\n  it('should not crash on malformed request', function (done) {\n    var upload = multer()\n\n    app.post('/upload', upload.single('file'), function (req, res) {\n      res.status(500).end('Request should not be processed')\n    })\n\n    app.use(function (err, req, res, next) {\n      assert.strictEqual(err.message, 'Unexpected end of form')\n      res.status(200).end('Correct error')\n    })\n\n    var boundary = 'AaB03x'\n    var body = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"',\n      'Content-Type: text/plain',\n      '',\n      'test without end boundary'\n    ].join('\\r\\n')\n    var options = {\n      hostname: 'localhost',\n      port,\n      path: '/upload',\n      method: 'POST',\n      headers: {\n        'content-type': 'multipart/form-data; boundary=' + boundary,\n        'content-length': body.length\n      }\n    }\n\n    var req = http.request(options, (res) => {\n      assert.strictEqual(res.statusCode, 200)\n      done()\n    })\n\n    req.on('error', (err) => {\n      done(err)\n    })\n\n    req.write(body)\n    req.end()\n  })\n\n  it('should not crash on malformed request that causes two errors to be emitted by busboy', function (done) {\n    var upload = multer()\n\n    app.post('/upload2', upload.single('file'), function (req, res) {\n      res.status(500).end('Request should not be processed')\n    })\n\n    app.use(function (err, req, res, next) {\n      assert.strictEqual(err.message, 'Malformed part header')\n      res.status(200).end('Correct error')\n    })\n\n    var boundary = 'AaB03x'\n    // this payload causes two errors to be emitted by busboy: `Malformed part header` and `Unexpected end of form`\n    var body = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"file\"; filename=\"test.txt\"',\n      'Content-Type: text/plain',\n      '',\n      '--' + boundary + '--',\n      ''\n    ].join('\\r\\n')\n    var options = {\n      hostname: 'localhost',\n      port,\n      path: '/upload2',\n      method: 'POST',\n      headers: {\n        'content-type': 'multipart/form-data; boundary=' + boundary,\n        'content-length': body.length\n      }\n    }\n\n    var req = http.request(options, (res) => {\n      assert.strictEqual(res.statusCode, 200)\n      done()\n    })\n\n    req.on('error', (err) => {\n      done(err)\n    })\n\n    req.write(body)\n    req.end()\n  })\n\n  it('should not crash on malformed multipart body with bad boundary', function (done) {\n    var upload = multer()\n\n    app.post('/upload3', upload.single('image'), function (req, res) {\n      res.status(500).end('Request should not be processed')\n    })\n\n    app.use(function (err, req, res, next) {\n      assert.strictEqual(err.message, 'Unexpected end of form')\n      res.status(200).end('Correct error')\n    })\n\n    var boundary = '----FormBoundary'\n    var body = [\n      '------FormBoundary',\n      'Content-Disposition: form-data; name=\"image\"; filename=\"\"',\n      'Content-Type: application/octet-stream',\n      '',\n      '', // empty content\n      '------FormBoundar' // intentionally malformed final boundary (missing 'y')\n    ].join('\\r\\n')\n\n    var options = {\n      hostname: 'localhost',\n      port,\n      path: '/upload3',\n      method: 'POST',\n      headers: {\n        'Content-Type': 'multipart/form-data; boundary=' + boundary,\n        'Content-Length': Buffer.byteLength(body)\n      }\n    }\n\n    var req = http.request(options, (res) => {\n      assert.strictEqual(res.statusCode, 200)\n      done()\n    })\n\n    req.write(body)\n    req.end()\n  })\n})\n"
  },
  {
    "path": "test/fields.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\nvar deepEqual = require('deep-equal')\nvar stream = require('stream')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\nvar testData = require('testdata-w3c-json-form')\n\ndescribe('Fields', function () {\n  var parser\n\n  before(function () {\n    parser = multer().fields([])\n  })\n\n  it('should process multiple fields', function (done) {\n    var form = new FormData()\n\n    form.append('name', 'Multer')\n    form.append('key', 'value')\n    form.append('abc', 'xyz')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert(deepEqual(req.body, {\n        name: 'Multer',\n        key: 'value',\n        abc: 'xyz'\n      }))\n      done()\n    })\n  })\n\n  it('should process empty fields', function (done) {\n    var form = new FormData()\n\n    form.append('name', 'Multer')\n    form.append('key', '')\n    form.append('abc', '')\n    form.append('checkboxfull', 'cb1')\n    form.append('checkboxfull', 'cb2')\n    form.append('checkboxhalfempty', 'cb1')\n    form.append('checkboxhalfempty', '')\n    form.append('checkboxempty', '')\n    form.append('checkboxempty', '')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert(deepEqual(req.body, {\n        name: 'Multer',\n        key: '',\n        abc: '',\n        checkboxfull: ['cb1', 'cb2'],\n        checkboxhalfempty: ['cb1', ''],\n        checkboxempty: ['', '']\n      }))\n      done()\n    })\n  })\n\n  it('should not process non-multipart POST request', function (done) {\n    var req = new stream.PassThrough()\n\n    req.end('name=Multer')\n    req.method = 'POST'\n    req.headers = {\n      'content-type': 'application/x-www-form-urlencoded',\n      'content-length': 11\n    }\n\n    parser(req, null, function (err) {\n      assert.ifError(err)\n      assert.strictEqual(Object.prototype.hasOwnProperty.call(req, 'body'), false)\n      assert.strictEqual(Object.prototype.hasOwnProperty.call(req, 'files'), false)\n      done()\n    })\n  })\n\n  it('should not process non-multipart GET request', function (done) {\n    var req = new stream.PassThrough()\n\n    req.end('name=Multer')\n    req.method = 'GET'\n    req.headers = {\n      'content-type': 'application/x-www-form-urlencoded',\n      'content-length': 11\n    }\n\n    parser(req, null, function (err) {\n      assert.ifError(err)\n      assert.strictEqual(Object.prototype.hasOwnProperty.call(req, 'body'), false)\n      assert.strictEqual(Object.prototype.hasOwnProperty.call(req, 'files'), false)\n      done()\n    })\n  })\n\n  testData.forEach(function (test) {\n    it('should handle ' + test.name, function (done) {\n      var form = new FormData()\n\n      test.fields.forEach(function (field) {\n        form.append(field.key, field.value)\n      })\n\n      util.submitForm(parser, form, function (err, req) {\n        assert.ifError(err)\n        assert(deepEqual(req.body, test.expected))\n        done()\n      })\n    })\n  })\n\n  it('should convert arrays into objects', function (done) {\n    var form = new FormData()\n\n    form.append('obj[0]', 'a')\n    form.append('obj[2]', 'c')\n    form.append('obj[x]', 'yz')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert(deepEqual(req.body, {\n        obj: {\n          0: 'a',\n          2: 'c',\n          x: 'yz'\n        }\n      }))\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/file-filter.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\n\nfunction withFilter (fileFilter) {\n  return multer({ fileFilter: fileFilter })\n}\n\nfunction skipSpecificFile (req, file, cb) {\n  cb(null, file.fieldname !== 'notme')\n}\n\nfunction reportFakeError (req, file, cb) {\n  cb(new Error('Fake error'))\n}\n\ndescribe('File Filter', function () {\n  it('should skip some files', function (done) {\n    var form = new FormData()\n    var upload = withFilter(skipSpecificFile)\n    var parser = upload.fields([\n      { name: 'notme', maxCount: 1 },\n      { name: 'butme', maxCount: 1 }\n    ])\n\n    form.append('notme', util.file('tiny0.dat'))\n    form.append('butme', util.file('tiny1.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert.strictEqual(req.files.notme, undefined)\n      assert.strictEqual(req.files.butme[0].fieldname, 'butme')\n      assert.strictEqual(req.files.butme[0].originalname, 'tiny1.dat')\n      assert.strictEqual(req.files.butme[0].size, 7)\n      assert.strictEqual(req.files.butme[0].buffer.length, 7)\n      done()\n    })\n  })\n\n  it('should report errors from fileFilter', function (done) {\n    var form = new FormData()\n    var upload = withFilter(reportFakeError)\n    var parser = upload.single('test')\n\n    form.append('test', util.file('tiny0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.strictEqual(err.message, 'Fake error')\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/file-ordering.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\n\ndescribe('File ordering', function () {\n  it('should present files in same order as they came', function (done) {\n    var storage = multer.memoryStorage()\n    var upload = multer({ storage: storage })\n    var parser = upload.array('themFiles', 2)\n\n    var i = 0\n    var calls = [{}, {}]\n    var pending = 2\n    var _handleFile = storage._handleFile\n    storage._handleFile = function (req, file, cb) {\n      var id = (i++)\n\n      _handleFile.call(this, req, file, function (err, info) {\n        if (err) return cb(err)\n\n        calls[id].cb = cb\n        calls[id].info = info\n\n        if (--pending === 0) {\n          calls[1].cb(null, calls[1].info)\n          calls[0].cb(null, calls[0].info)\n        }\n      })\n    }\n\n    var form = new FormData()\n\n    form.append('themFiles', util.file('small0.dat'))\n    form.append('themFiles', util.file('small1.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert.strictEqual(req.files.length, 2)\n      assert.strictEqual(req.files[0].originalname, 'small0.dat')\n      assert.strictEqual(req.files[1].originalname, 'small1.dat')\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/functionality.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar temp = require('fs-temp')\nvar rimraf = require('rimraf')\nvar FormData = require('form-data')\n\nfunction generateFilename (req, file, cb) {\n  cb(null, file.fieldname + file.originalname)\n}\n\nfunction startsWith (str, start) {\n  return (str.substring(0, start.length) === start)\n}\n\ndescribe('Functionality', function () {\n  var cleanup = []\n\n  function makeStandardEnv (cb) {\n    temp.mkdir(function (err, uploadDir) {\n      if (err) return cb(err)\n\n      cleanup.push(uploadDir)\n\n      var storage = multer.diskStorage({\n        destination: uploadDir,\n        filename: generateFilename\n      })\n\n      cb(null, {\n        upload: multer({ storage: storage }),\n        uploadDir: uploadDir,\n        form: new FormData()\n      })\n    })\n  }\n\n  after(function () {\n    while (cleanup.length) rimraf.sync(cleanup.pop())\n  })\n\n  it('should upload the file to the `dest` dir', function (done) {\n    makeStandardEnv(function (err, env) {\n      if (err) return done(err)\n\n      var parser = env.upload.single('small0')\n      env.form.append('small0', util.file('small0.dat'))\n\n      util.submitForm(parser, env.form, function (err, req) {\n        assert.ifError(err)\n        assert.ok(startsWith(req.file.path, env.uploadDir))\n        assert.strictEqual(util.fileSize(req.file.path), 1778)\n        done()\n      })\n    })\n  })\n\n  it('should rename the uploaded file', function (done) {\n    makeStandardEnv(function (err, env) {\n      if (err) return done(err)\n\n      var parser = env.upload.single('small0')\n      env.form.append('small0', util.file('small0.dat'))\n\n      util.submitForm(parser, env.form, function (err, req) {\n        assert.ifError(err)\n        assert.strictEqual(req.file.filename, 'small0small0.dat')\n        done()\n      })\n    })\n  })\n\n  it('should ensure all req.files values (single-file per field) point to an array', function (done) {\n    makeStandardEnv(function (err, env) {\n      if (err) return done(err)\n\n      var parser = env.upload.single('tiny0')\n      env.form.append('tiny0', util.file('tiny0.dat'))\n\n      util.submitForm(parser, env.form, function (err, req) {\n        assert.ifError(err)\n        assert.strictEqual(req.file.filename, 'tiny0tiny0.dat')\n        done()\n      })\n    })\n  })\n\n  it('should ensure all req.files values (multi-files per field) point to an array', function (done) {\n    makeStandardEnv(function (err, env) {\n      if (err) return done(err)\n\n      var parser = env.upload.array('themFiles', 2)\n      env.form.append('themFiles', util.file('small0.dat'))\n      env.form.append('themFiles', util.file('small1.dat'))\n\n      util.submitForm(parser, env.form, function (err, req) {\n        assert.ifError(err)\n        assert.strictEqual(req.files.length, 2)\n        assert.strictEqual(req.files[0].filename, 'themFilessmall0.dat')\n        assert.strictEqual(req.files[1].filename, 'themFilessmall1.dat')\n        done()\n      })\n    })\n  })\n\n  it('should rename the destination directory to a different directory', function (done) {\n    var storage = multer.diskStorage({\n      destination: function (req, file, cb) {\n        temp.template('testforme-%s').mkdir(function (err, uploadDir) {\n          if (err) return cb(err)\n\n          cleanup.push(uploadDir)\n          cb(null, uploadDir)\n        })\n      },\n      filename: generateFilename\n    })\n\n    var form = new FormData()\n    var upload = multer({ storage: storage })\n    var parser = upload.array('themFiles', 2)\n\n    form.append('themFiles', util.file('small0.dat'))\n    form.append('themFiles', util.file('small1.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert.strictEqual(req.files.length, 2)\n      assert.ok(req.files[0].path.indexOf('/testforme-') >= 0)\n      assert.ok(req.files[1].path.indexOf('/testforme-') >= 0)\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/issue-232.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar temp = require('fs-temp')\nvar rimraf = require('rimraf')\nvar FormData = require('form-data')\n\ndescribe('Issue #232', function () {\n  var uploadDir, upload\n\n  before(function (done) {\n    temp.mkdir(function (err, path) {\n      if (err) return done(err)\n\n      uploadDir = path\n      upload = multer({ dest: path, limits: { fileSize: 100 } })\n      done()\n    })\n  })\n\n  after(function (done) {\n    rimraf(uploadDir, done)\n  })\n\n  it('should report limit errors', function (done) {\n    var form = new FormData()\n    var parser = upload.single('file')\n\n    form.append('file', util.file('large.jpg'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ok(err, 'an error was given')\n\n      assert.strictEqual(err.code, 'LIMIT_FILE_SIZE')\n      assert.strictEqual(err.field, 'file')\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/memory-storage.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\nvar deepEqual = require('deep-equal')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\n\ndescribe('Memory Storage', function () {\n  var upload\n\n  before(function (done) {\n    upload = multer()\n    done()\n  })\n\n  it('should process multipart/form-data POST request', function (done) {\n    var form = new FormData()\n    var parser = upload.single('small0')\n\n    form.append('name', 'Multer')\n    form.append('small0', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.body.name, 'Multer')\n\n      assert.strictEqual(req.file.fieldname, 'small0')\n      assert.strictEqual(req.file.originalname, 'small0.dat')\n      assert.strictEqual(req.file.size, 1778)\n      assert.strictEqual(req.file.buffer.length, 1778)\n\n      done()\n    })\n  })\n\n  it('should process empty fields and an empty file', function (done) {\n    var form = new FormData()\n    var parser = upload.single('empty')\n\n    form.append('empty', util.file('empty.dat'))\n    form.append('name', 'Multer')\n    form.append('version', '')\n    form.append('year', '')\n    form.append('checkboxfull', 'cb1')\n    form.append('checkboxfull', 'cb2')\n    form.append('checkboxhalfempty', 'cb1')\n    form.append('checkboxhalfempty', '')\n    form.append('checkboxempty', '')\n    form.append('checkboxempty', '')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.body.name, 'Multer')\n      assert.strictEqual(req.body.version, '')\n      assert.strictEqual(req.body.year, '')\n\n      assert(deepEqual(req.body.checkboxfull, ['cb1', 'cb2']))\n      assert(deepEqual(req.body.checkboxhalfempty, ['cb1', '']))\n      assert(deepEqual(req.body.checkboxempty, ['', '']))\n\n      assert.strictEqual(req.file.fieldname, 'empty')\n      assert.strictEqual(req.file.originalname, 'empty.dat')\n      assert.strictEqual(req.file.size, 0)\n      assert.strictEqual(req.file.buffer.length, 0)\n      assert.strictEqual(Buffer.isBuffer(req.file.buffer), true)\n\n      done()\n    })\n  })\n\n  it('should process multiple files', function (done) {\n    var form = new FormData()\n    var parser = upload.fields([\n      { name: 'empty', maxCount: 1 },\n      { name: 'tiny0', maxCount: 1 },\n      { name: 'tiny1', maxCount: 1 },\n      { name: 'small0', maxCount: 1 },\n      { name: 'small1', maxCount: 1 },\n      { name: 'medium', maxCount: 1 },\n      { name: 'large', maxCount: 1 }\n    ])\n\n    form.append('empty', util.file('empty.dat'))\n    form.append('tiny0', util.file('tiny0.dat'))\n    form.append('tiny1', util.file('tiny1.dat'))\n    form.append('small0', util.file('small0.dat'))\n    form.append('small1', util.file('small1.dat'))\n    form.append('medium', util.file('medium.dat'))\n    form.append('large', util.file('large.jpg'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n\n      assert(deepEqual(req.body, {}))\n\n      assert.strictEqual(req.files.empty[0].fieldname, 'empty')\n      assert.strictEqual(req.files.empty[0].originalname, 'empty.dat')\n      assert.strictEqual(req.files.empty[0].size, 0)\n      assert.strictEqual(req.files.empty[0].buffer.length, 0)\n\n      assert.strictEqual(req.files.tiny0[0].fieldname, 'tiny0')\n      assert.strictEqual(req.files.tiny0[0].originalname, 'tiny0.dat')\n      assert.strictEqual(req.files.tiny0[0].size, 122)\n      assert.strictEqual(req.files.tiny0[0].buffer.length, 122)\n\n      assert.strictEqual(req.files.tiny1[0].fieldname, 'tiny1')\n      assert.strictEqual(req.files.tiny1[0].originalname, 'tiny1.dat')\n      assert.strictEqual(req.files.tiny1[0].size, 7)\n      assert.strictEqual(req.files.tiny1[0].buffer.length, 7)\n\n      assert.strictEqual(req.files.small0[0].fieldname, 'small0')\n      assert.strictEqual(req.files.small0[0].originalname, 'small0.dat')\n      assert.strictEqual(req.files.small0[0].size, 1778)\n      assert.strictEqual(req.files.small0[0].buffer.length, 1778)\n\n      assert.strictEqual(req.files.small1[0].fieldname, 'small1')\n      assert.strictEqual(req.files.small1[0].originalname, 'small1.dat')\n      assert.strictEqual(req.files.small1[0].size, 315)\n      assert.strictEqual(req.files.small1[0].buffer.length, 315)\n\n      assert.strictEqual(req.files.medium[0].fieldname, 'medium')\n      assert.strictEqual(req.files.medium[0].originalname, 'medium.dat')\n      assert.strictEqual(req.files.medium[0].size, 13196)\n      assert.strictEqual(req.files.medium[0].buffer.length, 13196)\n\n      assert.strictEqual(req.files.large[0].fieldname, 'large')\n      assert.strictEqual(req.files.large[0].originalname, 'large.jpg')\n      assert.strictEqual(req.files.large[0].size, 2413677)\n      assert.strictEqual(req.files.large[0].buffer.length, 2413677)\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/none.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\n\ndescribe('None', function () {\n  var parser\n\n  before(function () {\n    parser = multer().none()\n  })\n\n  it('should not allow file uploads', function (done) {\n    var form = new FormData()\n\n    form.append('key1', 'val1')\n    form.append('key2', 'val2')\n    form.append('file', util.file('small0.dat'))\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ok(err)\n      assert.strictEqual(err.code, 'LIMIT_UNEXPECTED_FILE')\n      assert.strictEqual(req.files, undefined)\n      assert.strictEqual(req.body.key1, 'val1')\n      assert.strictEqual(req.body.key2, 'val2')\n      done()\n    })\n  })\n\n  it('should handle text fields', function (done) {\n    var form = new FormData()\n\n    form.append('key1', 'val1')\n    form.append('key2', 'val2')\n\n    util.submitForm(parser, form, function (err, req) {\n      assert.ifError(err)\n      assert.strictEqual(req.files, undefined)\n      assert.strictEqual(req.body.key1, 'val1')\n      assert.strictEqual(req.body.key2, 'val2')\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/reuse-middleware.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\n\ndescribe('Reuse Middleware', function () {\n  var parser\n\n  before(function (done) {\n    parser = multer().array('them-files')\n    done()\n  })\n\n  it('should accept multiple requests', function (done) {\n    var pending = 8\n\n    function submitData (fileCount) {\n      var form = new FormData()\n\n      form.append('name', 'Multer')\n      form.append('files', '' + fileCount)\n\n      for (var i = 0; i < fileCount; i++) {\n        form.append('them-files', util.file('small0.dat'))\n      }\n\n      util.submitForm(parser, form, function (err, req) {\n        assert.ifError(err)\n\n        assert.strictEqual(req.body.name, 'Multer')\n        assert.strictEqual(req.body.files, '' + fileCount)\n        assert.strictEqual(req.files.length, fileCount)\n\n        req.files.forEach(function (file) {\n          assert.strictEqual(file.fieldname, 'them-files')\n          assert.strictEqual(file.originalname, 'small0.dat')\n          assert.strictEqual(file.size, 1778)\n          assert.strictEqual(file.buffer.length, 1778)\n        })\n\n        if (--pending === 0) done()\n      })\n    }\n\n    submitData(9)\n    submitData(1)\n    submitData(5)\n    submitData(7)\n    submitData(2)\n    submitData(8)\n    submitData(3)\n    submitData(4)\n  })\n})\n"
  },
  {
    "path": "test/select-field.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar util = require('./_util')\nvar multer = require('../')\nvar FormData = require('form-data')\n\nfunction generateForm () {\n  var form = new FormData()\n\n  form.append('CA$|-|', util.file('empty.dat'))\n  form.append('set-1', util.file('tiny0.dat'))\n  form.append('set-1', util.file('empty.dat'))\n  form.append('set-1', util.file('tiny1.dat'))\n  form.append('set-2', util.file('tiny1.dat'))\n  form.append('set-2', util.file('tiny0.dat'))\n  form.append('set-2', util.file('empty.dat'))\n\n  return form\n}\n\nfunction assertSet (files, setName, fileNames) {\n  var len = fileNames.length\n\n  assert.strictEqual(files.length, len)\n\n  for (var i = 0; i < len; i++) {\n    assert.strictEqual(files[i].fieldname, setName)\n    assert.strictEqual(files[i].originalname, fileNames[i])\n  }\n}\n\ndescribe('Select Field', function () {\n  var parser\n\n  before(function () {\n    parser = multer().fields([\n      { name: 'CA$|-|', maxCount: 1 },\n      { name: 'set-1', maxCount: 3 },\n      { name: 'set-2', maxCount: 3 }\n    ])\n  })\n\n  it('should select the first file with fieldname', function (done) {\n    util.submitForm(parser, generateForm(), function (err, req) {\n      assert.ifError(err)\n\n      var file\n\n      file = req.files['CA$|-|'][0]\n      assert.strictEqual(file.fieldname, 'CA$|-|')\n      assert.strictEqual(file.originalname, 'empty.dat')\n\n      file = req.files['set-1'][0]\n      assert.strictEqual(file.fieldname, 'set-1')\n      assert.strictEqual(file.originalname, 'tiny0.dat')\n\n      file = req.files['set-2'][0]\n      assert.strictEqual(file.fieldname, 'set-2')\n      assert.strictEqual(file.originalname, 'tiny1.dat')\n\n      done()\n    })\n  })\n\n  it('should select all files with fieldname', function (done) {\n    util.submitForm(parser, generateForm(), function (err, req) {\n      assert.ifError(err)\n\n      assertSet(req.files['CA$|-|'], 'CA$|-|', ['empty.dat'])\n      assertSet(req.files['set-1'], 'set-1', ['tiny0.dat', 'empty.dat', 'tiny1.dat'])\n      assertSet(req.files['set-2'], 'set-2', ['tiny1.dat', 'tiny0.dat', 'empty.dat'])\n\n      done()\n    })\n  })\n})\n"
  },
  {
    "path": "test/unicode.js",
    "content": "/* eslint-env mocha */\n\nvar assert = require('assert')\n\nvar multer = require('../')\nvar temp = require('fs-temp')\nvar rimraf = require('rimraf')\nvar stream = require('stream')\n\ndescribe('Unicode', function () {\n  var uploadDir, upload\n\n  beforeEach(function (done) {\n    temp.mkdir(function (err, path) {\n      if (err) return done(err)\n\n      var storage = multer.diskStorage({\n        destination: path,\n        filename: function (req, file, cb) {\n          cb(null, file.originalname)\n        }\n      })\n\n      uploadDir = path\n      upload = multer({ storage: storage })\n      done()\n    })\n  })\n\n  afterEach(function (done) {\n    rimraf(uploadDir, done)\n  })\n\n  it('should handle unicode filenames', function (done) {\n    var req = new stream.PassThrough()\n    var boundary = 'AaB03x'\n    var body = [\n      '--' + boundary,\n      'Content-Disposition: form-data; name=\"small0\"; filename=\"poo.dat\"; filename*=utf-8\\'\\'%F0%9F%92%A9.dat',\n      'Content-Type: text/plain',\n      '',\n      'test with unicode filename',\n      '--' + boundary + '--'\n    ].join('\\r\\n')\n\n    req.headers = {\n      'content-type': 'multipart/form-data; boundary=' + boundary,\n      'content-length': body.length\n    }\n\n    req.end(body)\n\n    upload.single('small0')(req, null, function (err) {\n      assert.ifError(err)\n\n      assert.strictEqual(req.file.originalname, '\\ud83d\\udca9.dat')\n      assert.strictEqual(req.file.fieldname, 'small0')\n\n      done()\n    })\n  })\n})\n"
  }
]