Configuração de Recursos Estáticos e Upload de Múltiplos Arquivos no Spring Boot

O Spring Boot possui um mecanismo automatizado para servir conteúdo estático. Por padrão, a aplicação busca e expõe arquivos localizados em diretórios específicos dentro do classpath. A ordem de prioridade para essa busca é a seguinte:

  • classpath:/META-INF/resources/
  • classpath:/resources/
  • classpath:/static/
  • classpath:/public/

Isso significa que um arquivo chamado image.png colocado na pasta src/main/resources/static/ poderá ser acessado diretamente através da URL http://localhost:8080/image.png.

Personalização de Diretórios e Caminhos Externos

É possível modificar tanto o padrão de requisição quanto os diretórios de busca através do arquivo application.properties. Além das rotas do classpath, o Spring Boot permite o mapeamento de diretórios absolutos no sistema de arquivos do servidor utilizando o prefixo file:.

Abaixo está um exemplo de configuração que expande os locais de busca padrão e inclui uma pasta externa no sistema operacional:

# Define o padrão de URL para interceptar requisições de arquivos estáticos
spring.mvc.static-path-pattern=/**

# Configuração dos locais de recursos (versões modernas do Spring Boot utilizam spring.web.resources)
# O prefixo 'file:' aponta para um caminho absoluto no disco
app.upload.dir=C:/uploads/
spring.web.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${app.upload.dir}

Com essa configuração, qualquer arquivo salvo no diretório C:/uploads/ será automaticamente servido pelo servidor web embutido.

Implementação de Upload de Arquivos

Para receber arquivos do cliente, o formulário HTML deve utilizar a codificação multipart/form-data. O atributo multiple permite o envio de vários arquivos simultaneamente.


<html lang="pt-BR">
<head>
    <meta charset="UTF-8">
    <title>Upload de Documentos</title>
</head>
<body>
    <h2>Envio de Arquivos</h2>
    <form action="/api/documents/upload" method="post" enctype="multipart/form-data">
        <label for="user">Identificação:</label>
        <input type="text" id="user" name="username" required>
        <br><br>
        <label for="docs">Selecione os arquivos:</label>
        <input type="file" id="docs" name="files" multiple required>
        <br><br>
        <button type="submit">Enviar</button>
    </form>
</body>
</html>

Processamanto no Backend

A abordagem mais robusta para manipular diretórios de upload em aplicações Spring Boot é injetar o caminho configurado via @Value, evitando o uso de ResourceUtils que pode apresentar comportamentos inconsistentes quando a aplicação é empacotada como um JAR executável. Utilizaremos a API java.nio.file para operações de sistema de arquivos mais seguras.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

@RestController
@RequestMapping("/api/documents")
public class DocumentUploadController {

    // Injeta o caminho definido no application.properties
    @Value("${app.upload.dir}")
    private String targetDirectory;

    @PostMapping("/upload")
    public String handleFileUpload(@RequestParam("files") MultipartFile[] uploadedFiles, 
                                   @RequestParam("username") String username) {
        
        if (uploadedFiles == null || uploadedFiles.length == 0) {
            return "Nenhum arquivo foi enviado.";
        }

        try {
            Path uploadPath = Paths.get(targetDirectory);
            
            // Garante que o diretório de destino exista
            if (!Files.exists(uploadPath)) {
                Files.createDirectories(uploadPath);
            }

            // Itera sobre o array de arquivos enviados
            for (MultipartFile file : uploadedFiles) {
                if (!file.isEmpty()) {
                    persistFile(file, uploadPath);
                }
            }
            return "Upload concluído com sucesso para o usuário: " + username;
            
        } catch (IOException e) {
            e.printStackTrace();
            return "Ocorreu um erro de I/O durante o upload.";
        }
    }

    private void persistFile(MultipartFile file, Path directory) throws IOException {
        String originalName = file.getOriginalFilename();
        String extension = "";
        
        // Extrai a extensão do arquivo original
        if (originalName != null && originalName.contains(".")) {
            extension = originalName.substring(originalName.lastIndexOf("."));
        }
        
        // Gera um nome único para evitar colisões
        String uniqueFileName = UUID.randomUUID().toString() + extension;
        Path destination = directory.resolve(uniqueFileName);
        
        // Transfere o fluxo de bytes para o sistema de arquivos
        file.transferTo(destination.toFile());
    }
}

Após o processamento da requisição POST, os documentos serão gravados no diretório definido em app.upload.dir. Como este diretório foi registrado no mapeamento de recursos estáticos, um arquivo salvo com o nome gerado 8f4e2a1b-...-.jpg ficará imediatamente acessível através da URL http://localhost:8080/8f4e2a1b-...-.jpg.

Tags: spring-boot static-resources multipart-upload java file-io

Publicado em 10-1 01:29