Em diversos cenários de desenvolvimento web, surge a necessidade de enviar, em uma única requisição HTTP, tanto um arquivo físico quanto um conjunot de informações estruturadas (um objeto ou entidade). No ecossistema AngularJS integrado a um backend Java (Spring/SSM), a estratégia mais eficiente consiste em conevrter o objeto em uma string JSON e anexá-la a um objeto FormData.
Estrutura do Formulário HTML
Para capturar os dados, utilizamos campos de entrada padrão vinculados ao modelo do AngularJS (ng-model) e um campo do tipo file para o documento.
<div class="form-group">
<label>Data de Registro:</label>
<input type="text" class="form-control" ng-model="registro.dataEmissao">
<label>Documento PDF:</label>
<input type="file" id="uploadAnexo" accept=".pdf">
<button class="btn btn-primary" ng-click="processarEnvio()">Salvar</button>
</div>
Lógica no Controller AngularJS
No controlador, o objeto que contém os dados textuais deve ser serializado para o formato string antes de ser processado pelo serviço de upload.
$scope.processarEnvio = function() {
// Serialização do objeto para String JSON
var jsonString = JSON.stringify($scope.registro);
uploadService.enviarParaServidor(jsonString)
.then(function(res) {
console.log("Sucesso:", res.data);
})
.catch(function(err) {
console.error("Erro no upload:", err);
});
};
Implementação do Serviço com FormData
O ponto crítico aqui é configurar o AngularJS para não aplicar sua serialização padrão de JSON no cabeçalho da requisição, permitindo que o navegador defina o boundary correto para o multipart/form-data.
this.enviarParaServidor = function(dadosJson) {
var fd = new FormData();
var inputArquivo = document.getElementById('uploadAnexo');
// Adiciona o arquivo e a string JSON ao FormData
fd.append("arquivo", inputArquivo.files[0]);
fd.append("metadados", dadosJson);
return $http.post("/api/documentos/upload", fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
});
};
Processamento no Backend (Spring MVC)
No lado do servidor, recebemos o arquivo como um MultipartFile e a string JSON como um parâmetro comum. Posteriormente, utilizamos uma biblioteca como Jackson ou FastJSON para converter a string de volta para um objeto Java.
@PostMapping("/upload")
public ResponseEntity<String> gerenciarUpload(
@RequestParam("arquivo") MultipartFile file,
@RequestParam("metadados") String metadadosJson) {
try {
// Conversão da String JSON para a Entidade Java
ObjectMapper mapper = new ObjectMapper();
EntidadeRegistro registro = mapper.readValue(metadadosJson, EntidadeRegistro.class);
System.out.println("Arquivo recebido: " + file.getOriginalFilename());
System.out.println("Entidade processada: " + registro.getNome());
return ResponseEntity.ok("Processamento concluído");
} catch (Exception e) {
return ResponseEntity.status(500).body("Erro interno: " + e.getMessage());
}
}
Ao definir o Content-Type como undefined no AngularJS, forçamos o navegador a preencher automaticamente o cabeçalho com multipart/form-data, incluindo o identificador de limite (boundary) necesário para que o servidor consiga distinguir onde termina o arquivo e onde começam os campos de texto da requisição.