Skip to content

Latest commit

 

History

History
841 lines (639 loc) · 37.1 KB

README_pt_BR.md

File metadata and controls

841 lines (639 loc) · 37.1 KB

npm formidable package logo

formidable npm version MIT license Libera Manifesto Twitter

A Node.js module for parsing form data, especially file uploads.

Code style codecoverage linux build status windows build status macos build status

Se você tiver qualquer tipo de pergunta sobre como fazer, por favor leia o Contributing Guia e Código de Conduta documentos.
Para relatórios de bugs e solicitações de recursos, crie uma issue ou ping @tunnckoCore / @3a1FcBx0 no Twitter.

Conventional Commits Minimum Required Nodejs Tidelift Subscription Buy me a Kofi Renovate App Status Make A Pull Request

Este projeto é semanticamente versionado e está disponível como parte da Assinatura Tidelift para nível profissional garantias, suporte aprimorado e segurança. Saiba mais.

Os mantenedores do formidable e milhares de outros pacotes estão trabalhando com Tidelift para fornecer suporte comercial e manutenção para o Open Source dependências que você usa para construir seus aplicativos. Economize tempo, reduza riscos e melhorar a integridade do código, enquanto paga aos mantenedores das dependências exatas que você usar.

Status do Projeto: Mantido

Verifique VERSION NOTES para obter mais informações sobre os planos v1, v2 e v3, NPM dist-tags e branches.

Este módulo foi inicialmente desenvolvido por @felixge para Transloadit, um serviço focado em upload e codificação de imagens e vídeos. Foi testado em batalha contra centenas de GBs de uploads de arquivos de uma grande variedade de clientes e é considerado pronto para produção e é usado na produção por anos.

Atualmente, somos poucos mantenedores tentando lidar com isso. :) Mais contribuidores são sempre bem-vindos! ❤️ Pule issue #412 que está fechado, mas se você estiver interessado, podemos discuti-lo e adicioná-lo após regras estritas, como ativar o Two-Factor Auth em suas contas npm e GitHub.

Destaques

Instalar

Este projeto requer Node.js >= 10.13. Instale-o usando yarn ou npm.
Nós altamente recomendamos usar o Yarn quando pensar em contribuir para este projeto.

Este é um pacote de baixo nível e, se você estiver usando uma estrutura de alto nível, pode já estar incluído. Verifique os exemplos abaixo e a pasta examples/.

# v2
npm install formidable
npm install formidable@v2

# v3
npm install formidable@v3

Nota: Em um futuro próximo, a v3 será publicada na dist-tag latest do NPM. Versões futuras não prontas serão publicadas nas dist-tags *-next para a versão correspondente.

Exemplos

Para mais exemplos veja o diretório examples/.

com módulo http Node.js

Analisar um upload de arquivo de entrada, com o Módulo http integrado do Node.js.

import http from 'node:http';
import formidable, {errors as formidableErrors} from 'formidable';

const server = http.createServer((req, res) => {
  if (req.url === '/api/upload' && req.method.toLowerCase() === 'post') {
    // analisar um upload de arquivo
    const form = formidable({});

    form.parse(req, (err, fields, files) => {
      if (err) {
        // exemplo para verificar um erro muito específico
        if (err.code === formidableErrors.maxFieldsExceeded) {

        }
        res.writeHead(err.httpCode || 400, { 'Content-Type': 'text/plain' });
        res.end(String(err));
        return;
      }
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ fields, files }, null, 2));
    });

    return;
  }

  // mostrar um formulário de upload de arquivo
  res.writeHead(200, { 'Content-Type': 'text/html' });
  res.end(`
    <h2>With Node.js <code>"http"</code> module</h2>
    <form action="/api/upload" enctype="multipart/form-data" method="post">
      <div>Text field title: <input type="text" name="title" /></div>
      <div>File: <input type="file" name="multipleFiles" multiple="multiple" /></div>
      <input type="submit" value="Upload" />
    </form>
  `);
});

server.listen(8080, () => {
  console.log('Server listening on http://localhost:8080/ ...');
});

com Express.js

Existem várias variantes para fazer isso, mas o Formidable só precisa do Node.js Request stream, então algo como o exemplo a seguir deve funcionar bem, sem nenhum middleware Express.js de terceiros.

Ou tente o examples/with-express.js

import express from 'express';
import formidable from 'formidable';

const app = express();

app.get('/', (req, res) => {
  res.send(`
    <h2>With <code>"express"</code> npm package</h2>
    <form action="/api/upload" enctype="multipart/form-data" method="post">
      <div>Text field title: <input type="text" name="title" /></div>
      <div>File: <input type="file" name="someExpressFiles" multiple="multiple" /></div>
      <input type="submit" value="Upload" />
    </form>
  `);
});

app.post('/api/upload', (req, res, next) => {
  const form = formidable({});

  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    res.json({ fields, files });
  });
});

app.listen(3000, () => {
  console.log('Server listening on http://localhost:3000 ...');
});

com Koa e Formidable

Claro, com Koa v1, v2 ou future v3 as coisas sao muito parecidas. Você pode usar formidable manualmente como mostrado abaixo ou através do pacote koa-better-body que é usando formidable sob o capô e suporte a mais recursos e diferentes corpos de solicitação, verifique sua documentação para mais informações.

Nota: este exemplo está assumindo Koa v2. Esteja ciente de que você deve passar ctx.req que é a solicitação do Node.js e NÃO o ctx.request que é a solicitação do Koa objeto - há uma diferença.

import Koa from 'Koa';
import formidable from 'formidable';

const app = new Koa();

app.on('error', (err) => {
  console.error('server error', err);
});

app.use(async (ctx, next) => {
  if (ctx.url === '/api/upload' && ctx.method.toLowerCase() === 'post') {
    const form = formidable({});
    
     // não muito elegante, mas é por enquanto se você não quiser usar `koa-better-body`
     // ou outros middlewares.
    await new Promise((resolve, reject) => {
      form.parse(ctx.req, (err, fields, files) => {
        if (err) {
          reject(err);
          return;
        }

        ctx.set('Content-Type', 'application/json');
        ctx.status = 200;
        ctx.state = { fields, files };
        ctx.body = JSON.stringify(ctx.state, null, 2);
        resolve();
      });
    });
    await next();
    return;
  }

  // mostrar um formulário de upload de arquivo
  ctx.set('Content-Type', 'text/html');
  ctx.status = 200;
  ctx.body = `
    <h2>With <code>"koa"</code> npm package</h2>
    <form action="/api/upload" enctype="multipart/form-data" method="post">
    <div>Text field title: <input type="text" name="title" /></div>
    <div>File: <input type="file" name="koaFiles" multiple="multiple" /></div>
    <input type="submit" value="Upload" />
    </form>
  `;
});

app.use((ctx) => {
  console.log('The next middleware is called');
  console.log('Results:', ctx.state);
});

app.listen(3000, () => {
  console.log('Server listening on http://localhost:3000 ...');
});

Benchmarks

O benchmark é bastante antigo, da antiga base de código. Mas talvez seja bem verdade. Anteriormente, os números giravam em torno de ~ 500 mb/s. Atualmente com a mudança para o novo Node.js Streams API, é mais rápido. Você pode ver claramente as diferenças entre as versões do Node.

Observação: um benchmarking muito melhor pode e deve ser feito no futuro.

Benchmark realizado em 8 GB de RAM, Xeon X3440 (2,53 GHz, 4 núcleos, 8 threads)

~/github/node-formidable master
❯ nve --parallel 8 10 12 13 node benchmark/bench-multipart-parser.js

 ⬢  Node 8

1261.08 mb/sec

 ⬢  Node 10

1113.04 mb/sec

 ⬢  Node 12

2107.00 mb/sec

 ⬢  Node 13

2566.42 mb/sec

benchmark 29 de janeiro de 2020

API

Formidable / IncomingForm

Todos os mostrados são equivalentes.

Por favor, passe options para a função/construtor, não atribuindo eles para a instância form

import formidable from 'formidable';
const form = formidable(options);

Opções

Veja seus padrões em src/Formidable.js DEFAULT_OPTIONS (a constante DEFAULT_OPTIONS).

  • options.encoding {string} - padrão 'utf-8'; define a codificação para campos de formulário de entrada,

  • options.uploadDir {string} - padrão os.tmpdir(); o diretório para colocar os uploads de arquivos. Você pode movê-los mais tarde usando fs.rename().

  • options.keepExtensions {boolean} - padrão false; incluir as extensões dos arquivos originais ou não

  • options.allowEmptyFiles {boolean} - padrão false; permitir upload de arquivos vazios

  • options.minFileSize {number} - padrão 1 (1byte); o tamanho mínimo do arquivo carregado.

  • options.maxFiles {number} - padrão Infinity; limitar a quantidade de arquivos carregados, defina Infinity para ilimitado

  • options.maxFileSize {number} - padrão 200 * 1024 * 1024 (200mb); limitar o tamanho de cada arquivo carregado.

  • options.maxTotalFileSize {number} - padrão options.maxFileSize; limitar o tamanho do lote de arquivos carregados.

  • options.maxFields {number} - padrão 1000; limite o número de campos, defina Infinity para ilimitado

  • options.maxFieldsSize {number} - padrão 20 * 1024 * 1024 (20mb); limitar a quantidade de memória que todos os campos juntos (exceto arquivos) podem alocar em bytes.

  • options.hashAlgorithm {string | false} - padrão false; incluir checksums calculados para arquivos recebidos, defina isso para algum algoritmo de hash, consulte crypto.createHash para algoritmos disponíveis

  • options.fileWriteStreamHandler {function} - padrão null, que por padrão grava no sistema de arquivos da máquina host cada arquivo analisado; A função deve retornar uma instância de um fluxo gravável que receberá os dados do arquivo carregado. Com esta opção, você pode ter qualquer comportamento personalizado em relação a onde os dados do arquivo carregado serão transmitidos. Se você deseja gravar o arquivo carregado em outros tipos de armazenamento em nuvem (AWS S3, armazenamento de blob do Azure, armazenamento em nuvem do Google) ou armazenamento de arquivo privado, esta é a opção que você está procurando. Quando esta opção é definida, o comportamento padrão de gravar o arquivo no sistema de arquivos da máquina host é perdido.

  • options.filename {function} - padrão undefined Use-o para controlar newFilename. Deve retornar uma string. Será associado a options.uploadDir.

  • options.filter {function} - função padrão que sempre retorna verdadeiro. Use-o para filtrar arquivos antes de serem carregados. Deve retornar um booleano.

options.filename {function} function (name, ext, part, form) -> string

onde a parte pode ser decomposta como

const { originalFilename, mimetype} = part;

Observação: Se este tamanho de campos combinados, ou tamanho de algum arquivo for excedido, um O evento 'error' é disparado.

// A quantidade de bytes recebidos para este formulário até agora.
form.bytesReceived;
// O número esperado de bytes neste formulário.
form.bytesExpected;

options.filter {function} function ({name, originalFilename, mimetype}) -> boolean

Observação: use uma variável externa para cancelar todos os uploads no primeiro erro

const options = {
  filter: function ({name, originalFilename, mimetype}) {
    // manter apenas imagens
    return mimetype && mimetype.includes("image");
  }
};

.parse(request, callback)

Analisa uma request do Node.js recebida contendo dados de formulário. Se callback for fornecido, todos os campos e arquivos são coletados e passados para o retorno de chamada.

const form = formidable({ uploadDir: __dirname });

form.parse(req, (err, fields, files) => {
  console.log('fields:', fields);
  console.log('files:', files);
});

Você pode substituir esse método se estiver interessado em acessar diretamente o fluxo de várias partes. Fazer isso desativará qualquer processamento de eventos 'field' / 'file' que ocorreria de outra forma, tornando você totalmente responsável por lidar com o processamento.

Sobre uploadDir, dada a seguinte estrutura de diretório

project-name
├── src
│   └── server.js
│       
└── uploads
    └── image.jpg

__dirname seria o mesmo diretório que o próprio arquivo de origem (src)

 `${__dirname}/../uploads`

para colocar arquivos em uploads.

Omitir __dirname tornaria o caminho relativo ao diretório de trabalho atual. Isso seria o mesmo se server.js fosse iniciado a partir de src, mas não de project-name.

null usará o padrão que é os.tmpdir()

Nota: Se o diretório não existir, os arquivos carregados são silenciosamente descartados. Para ter certeza de que existe:

import {createNecessaryDirectoriesSync} from "filesac";


const uploadPath = `${__dirname}/../uploads`;
createNecessaryDirectoriesSync(`${uploadPath}/x`);

No exemplo abaixo, escutamos alguns eventos e os direcionamos para o ouvinte data, para que você possa fazer o que quiser lá, com base em se é antes do arquivo ser emitido, o valor do cabeçalho, o nome do cabeçalho, no campo , em arquivo e etc.

Ou a outra maneira poderia ser apenas substituir o form.onPart como é mostrado um pouco mais tarde.

form.once('error', console.error);

form.on('fileBegin', (formname, file) => {
  form.emit('data', { name: 'fileBegin', formname, value: file });
});

form.on('file', (formname, file) => {
  form.emit('data', { name: 'file', formname, value: file });
});

form.on('field', (fieldName, fieldValue) => {
  form.emit('data', { name: 'field', key: fieldName, value: fieldValue });
});

form.once('end', () => {
  console.log('Done!');
});

// Se você quiser personalizar o que quiser...
form.on('data', ({ name, key, value, buffer, start, end, formname, ...more }) => {
  if (name === 'partBegin') {
  }
  if (name === 'partData') {
  }
  if (name === 'headerField') {
  }
  if (name === 'headerValue') {
  }
  if (name === 'headerEnd') {
  }
  if (name === 'headersEnd') {
  }
  if (name === 'field') {
    console.log('field name:', key);
    console.log('field value:', value);
  }
  if (name === 'file') {
    console.log('file:', formname, value);
  }
  if (name === 'fileBegin') {
    console.log('fileBegin:', formname, value);
  }
});

.use(plugin: Plugin)

Um método que permite estender a biblioteca Formidable. Por padrão, incluímos 4 plug-ins, que são essencialmente adaptadores para conectar os diferentes analisadores integrados.

Os plugins adicionados por este método estão sempre ativados.

Consulte src/plugins/ para uma visão mais detalhada dos plug-ins padrão.

O parâmetro plugin tem essa assinatura:

function(formidable: Formidable, options: Options): void;

A arquitetura é simples. O plugin é uma função que é passada com a instância Formidable (o form nos exemplos README) e as opções.

Observação: o contexto this da função do plug-in também é a mesma instância.

const form = formidable({ keepExtensions: true });

form.use((self, options) => {
  // self === this === form
  console.log('woohoo, custom plugin');
  // faça suas coisas; verifique `src/plugins` para inspiração
});

form.parse(req, (error, fields, files) => {
  console.log('done!');
});

Importante observar, é que dentro do plugin this.options, self.options e options PODEM ou NÃO ser iguais. A melhor prática geral é sempre usar o this, para que você possa testar seu plugin mais tarde de forma independente e mais fácil.

Se você quiser desabilitar alguns recursos de análise do Formidable, você pode desabilitar o plugin que corresponde ao analisador. Por exemplo, se você deseja desabilitar a análise de várias partes (para que o src/parsers/Multipart.js que é usado em src/plugins/multipart.js), então você pode removê-lo do options.enabledPlugins, assim

import formidable, {octetstream, querystring, json} from "formidable";
const form = formidable({
  hashAlgorithm: 'sha1',
  enabledPlugins: [octetstream, querystring, json],
});

Esteja ciente de que a ordem PODE ser importante também. Os nomes correspondem 1:1 a arquivos na pasta src/plugins/.

Solicitações pull para novos plug-ins integrados PODEM ser aceitas - por exemplo, analisador de querystring mais avançado. Adicione seu plugin como um novo arquivo na pasta src/plugins/ (em letras minúsculas) e siga como os outros plugins são feitos.

form.onPart

Se você quiser usar Formidable para manipular apenas algumas partes para você, você pode fazer alguma coisa similar. ou ver #387 para inspiração, você pode, por exemplo, validar o tipo mime.

const form = formidable();

form.onPart = (part) => {
  part.on('data', (buffer) => {
    // faça o que quiser aqui
  });
};

Por exemplo, force Formidable a ser usado apenas em "partes" que não sejam de arquivo (ou seja, html Campos)

const form = formidable();

form.onPart = function (part) {
  // deixe formidável lidar apenas com partes não arquivadas
  if (part.originalFilename === '' || !part.mimetype) {
    // usado internamente, por favor, não substitua!
    form._handlePart(part);
  }
};

Arquivo

export interface File {
   // O tamanho do arquivo enviado em bytes.
   // Se o arquivo ainda estiver sendo carregado (veja o evento `'fileBegin'`),
   // esta propriedade diz quantos bytes do arquivo já foram gravados no disco.
  file.size: number;

   // O caminho em que este arquivo está sendo gravado. Você pode modificar isso no evento `'fileBegin'`
   // caso você esteja insatisfeito com a forma como o formidable gera um caminho temporário para seus arquivos.
  file.filepath: string;

  // O nome que este arquivo tinha de acordo com o cliente de upload.
  file.originalFilename: string | null;
  
  // calculado com base nas opções fornecidas.
  file.newFilename: string | null;

  // O tipo mime deste arquivo, de acordo com o cliente de upload.
  file.mimetype: string | null;

  // Um objeto Date (ou `null`) contendo a hora em que este arquivo foi gravado pela última vez.
  // Principalmente aqui para compatibilidade com o [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
  file.mtime: Date | null;

  file.hashAlgorithm: false | |'sha1' | 'md5' | 'sha256'
  // Se o cálculo `options.hashAlgorithm` foi definido, você pode ler o resumo hexadecimal desta var (no final, será uma string)
  file.hash: string | object | null;
}

file.toJSON()

Este método retorna uma representação JSON do arquivo, permitindo que você JSON.stringify() o arquivo que é útil para registrar e responder a solicitações.

Eventos

'progress'

Emitido após cada bloco de entrada de dados que foi analisado. Pode ser usado para rolar sua própria barra de progresso. Aviso Use isso apenas para a barra de progresso do lado do servidor. No lado do cliente, é melhor usar XMLHttpRequest com xhr.upload.onprogress =

form.on('progress', (bytesReceived, bytesExpected) => {});

'field'

Emitido sempre que um par campo/valor é recebido.

form.on('field', (name, value) => {});

'fileBegin'

Emitido sempre que um novo arquivo é detectado no fluxo de upload. Use este evento se desejar transmitir o arquivo para outro lugar enquanto armazena o upload no sistema de arquivos.

form.on('fileBegin', (formName, file) => {
     // acessível aqui
     // formName o nome no formulário (<input name="thisname" type="file">) ou http filename para octetstream
     // file.originalFilename http filename ou null se houver um erro de análise
     // file.newFilename gerou hexoid ou o que options.filename retornou
     // file.filepath nome do caminho padrão de acordo com options.uploadDir e options.filename
     // file.filepath = CUSTOM_PATH // para alterar o caminho final
});

'file'

Emitido sempre que um par campo/arquivo é recebido. file é uma instância de File.

form.on('file', (formname, file) => {
     // o mesmo que fileBegin, exceto
     // é muito tarde para alterar file.filepath
     // file.hash está disponível se options.hash foi usado
});

'error'

Emitido quando há um erro no processamento do formulário recebido. Uma solicitação que apresenta um erro é pausada automaticamente, você terá que chamar manualmente request.resume() se você quiser que a requisição continue disparando eventos 'data'.

Pode ter error.httpCode e error.code anexados.

form.on('error', (err) => {});

'aborted'

Emitido quando a requisição foi abortada pelo usuário. Agora isso pode ser devido a um evento 'timeout' ou 'close' no soquete. Após este evento ser emitido, um O evento error seguirá. No futuro, haverá um 'timeout' separado evento (precisa de uma mudança no núcleo do nó).

form.on('aborted', () => {});

'end'

Emitido quando toda a solicitação foi recebida e todos os arquivos contidos foram liberados para o disco. Este é um ótimo lugar para você enviar sua resposta.

form.on('end', () => {});

Helpers

firstValues

Obtém os primeiros valores dos campos, como pré 3.0.0 sem passar múltiplos em uma lista de exceções opcionais onde arrays de strings ainda são desejados (<select multiple> por exemplo)

import { firstValues } from 'formidable/src/helpers/firstValues.js';

// ...
form.parse(request, async (error, fieldsMultiple, files) => {
    if (error) {
        //...
    }
    const exceptions = ['thisshouldbeanarray'];
    const fieldsSingle = firstValues(form, fieldsMultiple, exceptions);
    // ...

readBooleans

Html form input type="checkbox" envia apenas o valor "on" se marcado, converta-o em booleanos para cada entrada que deve ser enviada como uma caixa de seleção, use somente após a chamada de firstValues ou similar.

import { firstValues } from 'formidable/src/helpers/firstValues.js';
import { readBooleans } from 'formidable/src/helpers/readBooleans.js';

// ...
form.parse(request, async (error, fieldsMultiple, files) => {
    if (error) {
        //...
    }
    const fieldsSingle = firstValues(form, fieldsMultiple);
    
    const expectedBooleans = ['checkbox1', 'wantsNewsLetter', 'hasACar'];
    const fieldsWithBooleans = readBooleans(fieldsSingle, expectedBooleans);
    // ...

Changelog

./CHANGELOG.md

Ports & Créditos

Contribuindo

Se a documentação não estiver clara ou tiver um erro de digitação, clique no botão Edit da página (ícone de lápis) e sugira uma correção. Se você gostaria de nos ajudar a corrigir um bug ou adicionar um novo recurso, verifique nosso [Contributing Guide][contribuindo-url]. Pull requests são bem-vindos!

Agradecimentos vão para essas pessoas maravilhosas (emoji key):


Felix Geisendörfer

💻 🎨 🤔 📖

Charlike Mike Reagent

🐛 🚇 🎨 💻 📖 💡 🤔 🚧 ⚠️

Kedar

💻 ⚠️ 💬 🐛

Walle Cyril

💬 🐛 💻 💵 🤔 🚧

Xargs

💬 🐛 💻 🚧

Amit-A

💬 🐛 💻

Charmander

💬 🐛 💻 🤔 🚧

Dylan Piercey

🤔

Adam Dobrawy

🐛 📖

amitrohatgi

🤔

Jesse Feng

🐛

Nathanael Demacon

💬 💻 👀

MunMunMiao

🐛

Gabriel Petrovay

🐛 💻

Philip Woods

💻 🤔

Dmitry Ivonin

📖

Claudio Poli

💻

De uma postagem do blog Felix:

  • Sven Lito por corrigir bugs e mesclar patches
  • egirshov por contribuir com muitas melhorias para o analisador multipartes formidável de nós
  • Andrew Kelley por também ajudar a corrigir bugs e fazer melhorias
  • Mike Frey por contribuir com suporte JSON

Licença

Formidable é licenciado sob a MIT License.