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.