Tenho um aplicativo frontend Angular com um backend Java no qual o usuário seleciona um documento e o salva.
Para fazer isso, tenho enviado um objeto FormData do frontend para o backend sem problemas.
Aqui está um trecho do que tenho atualmente no frontend que funciona para enviar o conteúdo de um único arquivo para o backend:
public postSaveDocument(entityType: DocumentEntityType, id: number, body: DocumentInputDto): Observable<Upload<UploadWithLockResultsDto>> {
const url = `${DocumentationService.baseUrl}/${getUrlSegment(entityType)}/${id}/documents`;
//Create a FormData object and set the values of the document
const formData = new FormData();
formData.append('documentId', this.documentId.toString());
formData.append('title', this.title);
formData.append('content', this.content, this.content.name);
return this.httpClient.post(url, formData,
{...DocumentationService.options, reportProgress: true, observe: 'events', responseType: 'json'})
.pipe(upload<UploadWithLockResultsDto>());
}
E aqui está o que eu faço no backend que funciona para salvar esse arquivo:
@PostMapping("/{documentEntityType}/{entityId}/documents")
public ResponseEntity<UploadWithLockResultsDto> saveDocument(
@PathVariable("documentEntityType") DocumentEntityType documentEntityType,
@PathVariable("entityId") Integer entityId,
DocumentInputDto documentInputDto,
) throws AuthException, IOException {
//Save the updates to the database
var uploadWithLockResultsDto = documentService.documentUpdate(documentInputDto, authentication.getName(), authentication.getPrincipal());
return handleUploadWithLockResults(uploadWithLockResultsDto);
}
E isso funciona muito bem para salvar um único documento.
Agora, o que eu gostaria de fazer é permitir que o usuário selecione mais de um documento por vez e salve-os de uma só vez conforme são enviados para o back-end.
Até agora não tive sorte com isso.
Tentei colocar os dados em uma matriz de objetos FormData no front-end
public postSaveDocuments(entityType: DocumentEntityType, id: number, body: DocumentInputDto[]): Observable<Upload<UploadWithLockResultsDto>> {
const formDataArray: any[] = [];
body.forEach(documentInputDto => {
const formData = new FormData();
formData.append('documentId', this.documentId.toString());
formData.append('title', this.title);
formData.append('content', this.content, this.content.name);
formDataArray.push(formData);
});
return this.httpClient.post(url, formDataArray,
{...DocumentationService.options, reportProgress: true, observe: 'events', responseType: 'json'})
.pipe(upload<UploadWithLockResultsDto>());
}
E então eu tento pegá-lo no backend como uma matriz
@PostMapping("/{documentEntityType}/{entityId}/documents")
public ResponseEntity<UploadWithLockResultsDto> saveDocument(
@PathVariable("documentEntityType") DocumentEntityType documentEntityType,
@PathVariable("entityId") Integer entityId,
DocumentInputDto[] documentInputDtos,
) throws AuthException, IOException {
//Save the docs to the database
var uploadWithLockResultsDto = documentService.updateDocuments(documentInputDtos, authentication.getName(), authentication.getPrincipal());
return handleUploadWithLockResults(uploadWithLockResultsDto);
}
Não é de surpreender que isso não funcione. Ele nunca chega ao backend. E percebo que um array de objetos FormData provavelmente não foi feito para ser enviado do frontend para o backend.
Alguém pode me indicar a direção certa para modificar a chamada de modo que mais de um objeto FormData seja enviado da frente para trás?
Muito obrigado.
Você está no caminho certo, mas enviar vários objetos FormData em um array diretamente do frontend para o backend não vai funcionar porque o objeto FormData deve ser enviado como uma entidade única. Em vez disso, você deve enviar os documentos como partes individuais em um único objeto FormData usando uma solicitação de formulário multiparte.
Etapa 1: Frontend - Modifique o objeto FormData para manipular vários arquivos Para enviar vários arquivos, você pode anexar os dados de cada documento ao mesmo objeto FormData com chaves exclusivas para cada documento. Isso permitirá que você envie vários documentos em uma solicitação HTTP.
Código de frontend atualizado:
Etapa 2: Backend - Manipule os dados do formulário multipartes No backend, você precisa manipular os vários arquivos e seus metadados associados. Você pode fazer isso aceitando os dados como uma matriz MultipartFile ou um DTO personalizado, dependendo de como você prefere estruturar a solicitação. Como você está enviando metadados e arquivos, pode usar uma combinação de anotações @RequestParam e @ModelAttribute para vincular os dados do formulário e o conteúdo do arquivo.
Código de backend atualizado:
Etapa 3: atualize seu DocumentInputDto
O problema que você está enfrentando com o array documents retornando como nulo é provavelmente por causa de como o Spring manipula dados de formulário e uploads de arquivos multipart. O problema pode ser que você esteja enviando arquivos e dados de formulário (como metadados) sob o mesmo nome (documents[]) em sua solicitação, mas o Spring pode não estar mapeando corretamente as partes MultipartFile para os documentos List.
Vamos analisar alguns ajustes que você pode fazer para garantir que o Spring mapeie corretamente os dados do arquivo e os metadados para os campos apropriados.
Etapa 1: Atualize o código do frontend Você já está enviando os arquivos com metadados em um único objeto FormData. A questão principal é garantir a estrutura correta da solicitação para que o backend possa identificar corretamente os arquivos e os metadados. Os arquivos precisam ser enviados com campos de nome separados para cada documento, e os metadados devem seguir a mesma convenção de nomenclatura.
Aqui está o código de frontend atualizado para anexar vários documentos corretamente:
Etapa 2: Atualizar o código de backend O Spring usa o @RequestParam para vincular dados de formulário e uploads de arquivo. A chave para resolver esse problema é garantir que os metadados e os arquivos estejam vinculados corretamente aos parâmetros de backend. Veja como você pode ajustar o backend para manipular corretamente os uploads de arquivo e metadados:
MultipartFile Array: @RequestParam do Spring mapeará vários arquivos enviados com o mesmo nome para uma List. Mapeamento de Metadados: Para mapear os metadados do documento (como documentId e title), você pode usar um map ou um DTO personalizado. Aqui está o método de backend atualizado:
Etapa 3: Garanta que DocumentInputDto esteja correto Certifique-se de que sua classe DocumentInputDto tenha um campo para o MultipartFile e que você esteja usando getters e setters corretamente. Aqui está uma versão simples da classe DocumentInputDto:
Etapa 4: Lidar com a configuração multipartes (se necessário) Certifique-se de que seu aplicativo Spring Boot esteja configurado para lidar com uploads de arquivos multipartes. Isso normalmente é feito no arquivo application.properties ou application.yml. Veja como você pode configurá-lo: