Upload de arquivos em Java

Upload de arquivos em Java

Surgiu a necessidade de implementar uma funcionalidade de envio de arquivos para o servidor, mas como podemos implementar o recurso de upload em nosso sistema?

Imagem de destaque #cover

Nosso primeiro passo será criar uma classe para representar qualquer tipo de arquivo, logo podemos ver que um bom nome para a mesma seria Arquivo:


public class Arquivo {

}

Criando o método upload

Após criar e definir o nome de nossa classe estamos pronto para o próximo passo, vamos começar a implementação do método upload:


public class Arquivo { 
  public void upload() 
  { // implementar o upload } 
}

Bacana, temos o nosso método para realizar o upload, mas por onde podemos começar? Inicialmente sabemos que um arquivo possui o nome e o lugar ao qual ele será gravado, portanto, podemos receber esses 2 parâmetros no nosso método:


public void upload(String pasta, String nomeDoArquivo) {
   // implementar o upload 
}

Quando fazemos upload de um arquivo, além de pegar o nome e o local, precisamos também pegar o próprio arquivo em si, ou melhor, precisamos pegar os seus bytes que o representa de verdade dentro do computador. Sendo assim, veremos passo a passo como podemos fazer isso.

Banner da Escola de Programação: Matricula-se na escola de Programação. Junte-se a uma comunidade de mais de 500 mil estudantes. Na Alura você tem acesso a todos os cursos em uma única assinatura; tem novos lançamentos a cada semana; desafios práticos. Clique e saiba mais!

Implementando o método Upload

Temos o caminho e nome do nosso arquivo, porém, como podemos representar um arquivo em Java? Fazemos isso por meio classe File passando o nome do arquivo completo em sua construção.


public void upload(String pasta, String nomeDoArquivo) { 

String caminhoArquivo = pasta + "/" + nomeDoArquivo; 
File novoArquivo = new File(caminhoArquivo); 
}

Agora já temos também a representação do nosso arquivo, portanto, o nosso próximo passo é entender como realmente funciona o upload de arquivos.

Transferindo arquivos em Java

No processo de upload é realizado a transferência do arquivo local em nossa máquina para o navegador, assim que o navegador termina de carregá-lo já somos capazes de pegar uma "representação" desse arquivo, com a representação desse arquivo precisamos agora de fato salvá-lo em nosso servidor.

Nesse momento você deve estar imaginando:

"Basta apenas pegar o arquivo passado pelo navegador e de uma forma mágica salvá-lo no servidor."

Mas as coisas não são tão simples assim, o que realmente é feito durante o processo de um upload é a leitura byte a byte do arquivo carregado e depois criado um novo arquivo passando os bytes lidos.

Copiando um arquivo byte a byte

Para realizar a cópia do arquivo enviado pelo usuário e posteriormante carregado pelo navegador devemos seguir alguns passos, o primeiro nada mais é do que criar nosso método responsável por tal operação:


private void copiar() {

}

Beleza, já temos nosso método declarado, mas por onde devemos começar sua implementação? Como mencionado anteriormente sabemos que para realizar uma cópia precisamos de origem e destino, logo são dois fortes candidatos á serem parâmetros para nosso método:


private void copiar(InputStream origem, OutputStream destino) {

}

Repare que declaramos a origem do tipo InputStream pois é o objeto responsável por leituras de arquivos em Java, logo podemos ver que OutputStream é o responsável pela escrita, ambos pertencem ao pacote java.io.

Sabemos também que iremos ter que ler os bytes do arquivo origem para escrevê-lo no arquivo destino, podemos fazer isso através de uma variável:


private void copiar(InputStream origem, OutputStream destino) {
   int bite = 0; 
   }

Como a palavra byte é reservada da linguagem Java criei a variável como bite, também precisamos definir um tamanho máximo do arquivo, fazemos isso através de um array:


private void copiar(InputStream origem, OutputStream destino) {
int bite = 0; byte[]
tamanhoMaximo = new byte[1024 * 8]; // 8KB }

Agora precisamos ler os bytes da origem um por um e ir copiando para o arquivo destino, enquanto os mesmos forem existentes, logo vemos a necessidade de realizar um loop:


private void copiar(InputStream origem, OutputStream destino) { 
byte\[\]
int bite = 0; 
tamanhoMaximo = new byte[1024 * 8]; // 8KB 
// enquanto bytes forem sendo lidos 
while((bite = origem.read(tamanhoMaximo)) >= 0) { 
// pegue o byte lido e escreva no destino 
  destino.write(tamanhoMaximo, 0, bite); 
  } 
}

Quando estamos trabalhando com operações de leitura e escrita devemos tratar algumas possíveis exceções, podemos fazer isso através de um try catch:


private void copiar(InputStream origem, OutputStream destino) {
int bite = 0; byte[] 
tamanhoMaximo = new byte[1024 * 8]; // 8KB
try { 
// enquanto bytes forem sendo lidos 
   while((bite = origem.read(tamanhoMaximo)) >= 0) { 
    // pegue o byte lido e escreva no destino 
     destino.write(tamanhoMaximo, 0, bite);
     } 
   } catch (IOException e) { 
     // TODO Auto-generated catch block e.printStackTrace();
      } 
  }

Agora que já sabemos como é feito realmente um upload de arquivo e já criamos o método para realizar a leitura e cópia do mesmo, devemos utilizá-lo no momento de realizar o upload para de fato copiar o arquivo carregado pelo navegador para nosso servidor.

Mas até agora não temos uma forma de ler o arquivo carregado pelo navegador, como resolver o problema? Podemos passar o arquivo via parâmetro para nosso método upload:


public void upload(String pasta, String nomeDoArquivo, InputStream arquivoCarregado) {
String caminhoArquivo = pasta + "/" + nomeDoArquivo;
File novoArquivo = new File(caminhoArquivo); }

Definimos o parâmetro do tipo InputStream pois sabemos que o mesmo trata-se de uma leitura, além disso também sabemos que nosso método copiar precisa de um segundo parâmetro, onde será gravado o arquivo. Já temos a representação do nosso arquivo, precisamos apenas criar um OutputStream para realizar a escrita:


public void upload(String pasta, String nomeDoArquivo, InputStream arquivoCarregado) {
String caminhoArquivo = pasta + "/" + nomeDoArquivo; 
File novoArquivo = new File(caminhoArquivo); 
FileOutputStream saida = new FileOutputStream(novoArquivo); 
}

Com isso já temos tudo o que é preciso para de fato chamar nosso método copiar:


public void upload(String pasta, String nomeDoArquivo, InputStream arquivoCarregado) { 
String caminhoArquivo = pasta + "/" + nomeDoArquivo; 
File novoArquivo = new File(caminhoArquivo); 
FileOutputStream saida = new FileOutputStream(novoArquivo); 
copiar(arquivoCarregado, saida);
}

Com a nossa classe Arquivo pronta, já devemos ser capazes de realizar o upload do arquivo, o próximo passo será mexer em nosso front-end, ou seja, criar um formulário para enviar o arquivo para nosso back-end:

Buscando arquivos com HTML

O primeiro passo seria criarmos um form, para que sejamos capazes de realizar a busca do arquivo em nossa máquina:


<form action="URL" type="get">

</form>

Mas somente a tag form não é capaz de realizar tal necessidade sozinha, precisamos de alguma forma conseguir buscar um arquivo em nossa máquina e enviá-lo ao navegador, mas como podemos criar tal recurso?

Conhecendo o input file

Entre os vários tipos de input's existentes para nosso formulário, um deles é do tipo file, com ele conseguimos buscar e carregar arquivos locais de nossa máquina para a web:


<form action="URL" type="get"> 
  <input name="upload" type="file">
</form>

Para finalizar, devemos criar um botão para conseguirmos realizar a submissão do form:


<form action="URL" type="get"> 
<input name="upload" type="file"> <button type="submit">Enviar</button> 
</form>

Com isso, teremos o seguinte resultado:

Enviar

Porém se tentarmos realizar o upload do arquivo ainda não iremos ser capazes de lê-lo em nosso código Java, por que isso está acontecendo?

Por padrão todas as informações são decodificadas ao serem submetidas por um formulário, isso é definido através da tag enctype que, quando não definida, tem seu valor padrão como application/x-www-form-urlencoded.

Para mudarmos esse comportamente padrão precisamos deixar explícito o valor do enctype para multipart/form-data:


<form action="URL" enctype="multipart/form-data" type="post"> // código omitido...

Enviando arquivo do navegador para o servidor

Para fazer o uso da nossa classe Arquivo, precisamos de alguma forma realizar uma requisição para nosso código Java, pegar a representação do arquivo e posteriormente fazer uso de nossos métodos,

Para maior facilidade no exemplo irei utilizar o framework VRaptor, capaz de realizar a comunicação web e Java, você pode estar utilizando o framework de sua preferência:


@Controller @Path("arquivo") public class ArquivoController {

@Post public void upload(UploadedFile upload) {
 Arquivo arquivo = new Arquivo();
 arquivo.upload("/home/matheus/arquivos", upload.getFileName(), upload.getFile()); 
 }
}

Com isso já devemos ser capazes de carregar e enviar nosso arquivo para o servidor.

Caso fique um pouco confuso sobre os objetos utilizados para gravar, ler ou criar arquivos, na alura temos uma formação Java que fala sobre o pacote java.io e várias outras particularidades da linguagem.

Veja outros artigos sobre Programação