Upload simultâneo de arquivos e metadados JSON com AngularJS e Spring

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.

Tags: AngularJS spring-mvc FormData file-upload Java-Web

Publicado em 7-11 04:04