我有一个带有 Java 后端的 Angular 前端应用程序,用户可以在其中选择一个文档然后保存它。
为此,我一直在将 FormData 对象从前端发送到后端,没有任何问题。
这是我当前在前端所拥有的用于将单个文件的内容发送到后端的片段:
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>());
}
以下是我在后端保存该文件的操作:
@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);
}
这对于保存单个文档来说非常好。
现在我想要做的是允许用户一次选择多个文档,并在将它们发送到后端时一次性保存它们。
到目前为止,这件事还没有进展。
我尝试将数据放入前端的 FormData 对象数组中
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>());
}
然后我尝试在后端将其作为数组提取出来
@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);
}
毫不奇怪,这不起作用。它甚至从未到达后端。我意识到 FormData 对象数组可能不应该从前端发送到后端。
有人能指出我正确的方向吗,如何修改调用,以便从前端向后端发送多个 FormData 对象?
非常感谢。
您的思路是对的,但直接从前端向后端发送数组中的多个 FormData 对象是行不通的,因为 FormData 对象应该作为单个实体发送。相反,您应该使用多部分表单请求将文档作为单个 FormData 对象中的单独部分发送。
步骤 1:前端 - 修改 FormData 对象以处理多个文件 要发送多个文件,您可以将每个文档的数据附加到同一个 FormData 对象,每个文档都有唯一的键。这样您就可以在一个 HTTP 请求中发送多个文档。
更新的前端代码:
第 2 步:后端 - 处理多部分表单数据 在后端,您需要处理多个文件及其相关元数据。您可以通过将数据作为 MultipartFile 数组或自定义 DTO 来执行此操作,具体取决于您喜欢如何构造请求。由于您要发送元数据和文件,因此可以使用 @RequestParam 和 @ModelAttribute 注释的组合来绑定表单数据和文件内容。
更新了后端代码:
步骤 3:更新 DocumentInputDto
您面临的文档数组返回为 null 的问题可能是由于 Spring 处理表单数据和多部分文件上传的方式所致。问题可能是您在请求中使用相同名称 (documents[]) 发送文件和表单数据(如元数据),但 Spring 可能未正确将 MultipartFile 部分映射到 List 文档。
让我们来看看可以进行的一些调整,以确保 Spring 将文件数据和元数据正确地映射到正确的字段。
步骤 1:更新前端代码 您已经在单个 FormData 对象中发送带有元数据的文件。关键问题是确保请求的结构正确,以便后端可以正确识别文件和元数据。需要为每个文档发送带有单独名称字段的文件,并且元数据应遵循相同的命名约定。
以下是更新后的前端代码,可以正确附加多个文档:
步骤 2:更新后端代码 Spring 使用 @RequestParam 绑定表单数据和文件上传。解决此问题的关键是确保元数据和文件正确绑定到后端参数。以下是如何调整后端以正确处理文件上传和元数据的方法:
MultipartFile Array:Spring 的 @RequestParam 会将发送的多个同名文件映射到 List。元数据映射:要映射文档元数据(例如 documentId 和 title),您可以使用映射或自定义 DTO。以下是更新后的后端方法:
步骤 3:确保 DocumentInputDto 正确 确保您的 DocumentInputDto 类具有 MultipartFile 字段,并且您正确使用了 getter 和 setter。以下是 DocumentInputDto 类的一个简单版本:
步骤 4:处理多部分配置(如果需要)确保您的 Spring Boot 应用程序已配置为处理多部分文件上传。这通常在 application.properties 或 application.yml 文件中完成。您可以按照以下方式进行配置: