Upload pro S3: o mais simples possível em Java

Upload File to S3 Java

Recentemente estive no QConSP 2013, onde encontrei muitos amigos e conversei bastante. Conversando com o Milfont, ele me falou que achava que ninguém mais faz upload de arquivos para um file system local, pois o S3 é muito conveniente e fácil de usar.

Nos projetos Ruby dele ele usa o Paperclip, que permite o upload pro S3 com simples configurações.

Porém, nos nossos projetos Amazon vemos que a grande maioria das aplicações ainda não faz os uploads para o S3, e isto me motivou a escrever posts mostrando da forma mais simples possível o upload pro S3 em algumas linguagens, frameworks e produtos.

Este post é o primeiro da série e nele vamos mostrar o upload convencional para o file system e o upload equivalente para o S3. Para a versão TL;DR, simplesmente faça o clone do github e experimente localmente.

git clone https://github.com/RivendelTecnologia/UploadS3Java.git

No diretório WebContent há 2 htmls bem simples, ambos com formulários contendo um único elemento, de upload de arquivo.

O arquivo disco.html aponta para o Servlet /UploadDisco, que manipula o stream de entrada da forma convencional (com o commons-fileupload) e salva em disco, conforme a seguir.

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		try {
			List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
			for (FileItem item : items) {
				if (item.isFormField()) {
					// nada a fazer, só temos o upload de arquivo no formulário
				} else {
					// Process form file field (input type="file").
					InputStream conteudoArquivo = item.getInputStream();
					File arquivo = new File(item.getName() + System.currentTimeMillis());
					FileOutputStream fos = new FileOutputStream(arquivo);
					int read = 0;
					byte[] bytes = new byte[1024];
					int contador = 0;

					while ((read = conteudoArquivo.read(bytes)) != -1) {
						fos.write(bytes, 0, read);
						contador++;
						System.out.println("escrevendo " + contador);
					}
					System.out.println("Pronto! Arquivo: " + arquivo.getAbsolutePath());
					conteudoArquivo.close();
					fos.flush();
					fos.close();
					response.getWriter().println("Arquivo escrito: " + arquivo.getAbsolutePath());
				}
			}
		} catch (FileUploadException e) {
			throw new ServletException("Cannot parse multipart request.", e);
		} finally{
			
		}

	}

Para usar a versão com o S3, precisamos baixar o SDK para Java e adicionar às nossas dependências, ou usar o Maven para isso. As funcionalidades do S3 dependem de algumas bibliotecas do Apache Commons.

Além do SDK no classpath, precisamos adicionar um arquivo AwsCredentials.properties, com sua accessKey e secretKey. Ambas podem ser obtidas na opção Security Credentials, na console da Amazon, conforme a seguir.

AWS security credentials

A última coisa é que precisamos ter um bucket S3 disponível para o upload. Podemos criar um novo bucket pela API também, mas a opção mais fácil e mais comum é usar um bucket pré-existente.

Colocamos abaixo o Servlet recebendo o upload do arquivo e enviando diretamente a stream para o S3, sem manipular arquivos no file system local.

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		AmazonS3 s3 = new AmazonS3Client(new ClasspathPropertiesFileCredentialsProvider());
		Region usWest2 = Region.getRegion(Regions.US_WEST_2);
		s3.setRegion(usWest2);
		String nomeBucket = "rivendel-upload-s3";
		String nomeArquivo = "";
		
		try {
			List<FileItem> items = new ServletFileUpload(new DiskFileItemFactory()).parseRequest(request);
			for (FileItem item : items) {
				if (item.isFormField()) {
					// nada a fazer, só temos o upload de arquivo no formulário
				} else {
					// Process form file field (input type="file").
					InputStream conteudoArquivo = item.getInputStream();
					ObjectMetadata metadata = new ObjectMetadata();
					metadata.setContentType(item.getContentType());
					metadata.setContentLength(item.getSize());
					nomeArquivo = item.getName();
					PutObjectRequest por = new PutObjectRequest(nomeBucket, nomeArquivo, conteudoArquivo, metadata);
					PutObjectResult result = s3.putObject(por.withCannedAcl(CannedAccessControlList.PublicRead));
					response.getWriter().println("Arquivo escrito: " + nomeArquivo);
				}
			}
		} catch (FileUploadException e) {
			throw new ServletException("Cannot parse multipart request.", e);
		} finally{
			
		}
	}

Neste upload, enviamos o arquivo para um bucket chamado rivendel-upload-s3, e definimos a sua política de acesso como público para leitura. Assim, qualquer pessoa poderá baixar o arquivo do S3 sem precisar de autenticação.

Com o nome da região e o nome do bucket é possível montar a URL de acesso ao arquivo, que é sempre no formato: https://s3-nome-regiao.amazonaws.com/nome-bucket/nome-arquivo.

Em breve publicaremos os posts equivalentes em várias outras linguagens. Espero que seja útil para muitas pessoas :). Até a próxima!

Gostou do conteúdo? Tem alguma dúvida? Entre em contato com nossos Especialistas Mandic Cloud, ficamos felizes em ajudá-lo.