Upload De Arquivos Em Java
Após realizar a entrega de um projeto web para nosso cliente, 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 ? Para o exemplo iremos fazer uso da linguagem Java.
Criando a classe Arquivo
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.
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) {
int bite = 0;
byte[] 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:
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.