问题一:
需要上传四个文件 如图:
可以发现这个四个需要上传文件都在一个文件夹下,后面为了方便用户上传就改成了上传一整个文件夹,
如图:
出现的一些问题,后台接收文件的时候出现了报错
后台使用的是HttpServletRequest接收的一整个文件夹。
@PostMapping({"folder/img/{id}"})
public void folderImgUpload(HttpServletRequest folderRequest,
@PathVariable Long id) {
if (folderRequest != null) {
this.invoiceOriginDataService.folderImgUpload(folderRequest, id);
} else {
throw new ValidationException("空文件夹");
}
}
public void folderImgUpload(HttpServletRequest folderRequest, Long id) {
InvoiceOriginData invoiceOriginData = this.invoiceOriginDataRepository.findById(id).orElseThrow(EntityNotFoundException::new);
System.out.println();
try {
for (Part part : folderRequest.getParts()) {
//截取图片名称
String newPath = invoiceOriginData.getPrefix() + invoiceOriginData.getPrefix() + this.invoiceProperty.getImagePath() + this.getFileName(part.getSubmittedFileName());
this.minioService.deleteObjects(invoiceOriginData.getBucket(), newPath);
this.minioService.uploadByte(invoiceOriginData.getBucket(), newPath, part.getInputStream().readAllBytes(), "image/jpeg");
}
} catch (Exception e) {
throw new RuntimeException("文件夹出现问题");
}
}
后面经过一系列排查 最终发现是{formData}搞得鬼
最后去掉 {},就上传成功了。
uploadImgs(files: FileList, invoiceOriginDataId: number): Observable<HttpEvent<object>> {
const formData: FormData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('file', files[i]);
}
return this.httpClient.post(`/document/folder/img/${invoiceOriginDataId}`,
formData ,
{reportProgress: true, observe: 'events'});
}
原因:
当使用 {} 作为参数时,Spring Boot 会将请求体中的 JSON 数据解析为 Java 对象。然而,文件上传通常不是以 JSON 格式传输的,而是使用 multipart/form-data 协议。因此,Spring Boot 无法正确解析包含文件的文件上传请求。
使用 formData 作为参数
FormData 类专门用于处理 multipart/form-data 类型的请求。当使用 formData 作为参数时,Spring Boot 会将 FormData 对象中的数据直接传递给控制器方法,包括文件数据。
问题二:
此问题就是文件过大引起的,可以继续看到之前是怎么上传文件的
可以看到是我们把文件方法到一起上传导致。
解决:
有多少个文件就上传多少次,不放在一起上传。
@Action()
uploadInvoiceImage(file: File, invoiceOriginDataId: number): Observable<HttpEvent<object>> {
const formData: FormData = new FormData();
formData.append('file', file);
return this.httpClient.post(`/invoiceGroup/file/uploadInvoiceImage/${invoiceOriginDataId}`,
formData, {reportProgress: true, observe: 'events'});
}
c层
from(this.invoiceImgs).pipe(
concatMap(invoiceImg => this.invoiceGroupService.uploadInvoiceImage(invoiceImg, this.invoiceOriginDataId)),
last()
).subscribe(lastInvoiceImg => {
this.invoiceGroupService.initAndPublishTasks(this.parentPath, this.invoiceOriginDataId).subscribe(invoiceGroup => {
this.invoiceGroupService.getInvoiceGroupById(invoiceGroup.id);
})
});
后台更改如下:
使用from将 this.invoiceImgs (数组)转换为一个 RxJS Observable 对象。
这个 Observable 会依次发出每个发票图像对象。然后配合concatMap 会等待每个上传完成才继续处理下一个发票图像。订阅最后一个,进行后续操作。
这个时候虽然解决了问题,但是检查网络的时候发现,并不是串行传输,是并行传输
concatMap不是串行发送吗,怎么又并行执行了呢.
仔细看可以看到是一个一个发送的,不是放在一起发送的。但是从图中可以看到,第一条还没执行完,第二条就开始执行了,以此类推,导致这种结果。
排查发现:
@Action()是action弄得鬼,导致出现这种状况,因为使用了状态管理中的action,只要调用就会执行,不要在订阅了。
此时就串行传出了。
const formData: FormData = new FormData();
formData.append('file', file);
return this.httpClient.post<Observable<HttpEvent<object>>>(`/invoiceGroup/file/uploadInvoiceImage`,
formData, {reportProgress: true, observe: 'events'});
}
//c层httpEvent.type === HttpEventType.Response
if (httpEvent.type === HttpEventType.Response)
from(this.invoiceImgs).pipe(
concatMap(invoiceImg => this.invoiceGroupService.uploadInvoiceImage(invoiceImg)),
last(),
).subscribe((httpEvent: HttpEvent<object>) => {
if (httpEvent.type === HttpEventType.Response) {
this.invoiceGroupService.initAndPublishTasks(this.parentPath).subscribe(invoiceGroup => {
this.invoiceGroupService.getInvoiceGroupById(invoiceGroup.id);
});
}
});
小结:
HttpEventTyp的几个类型
HttpEventType.Sent:表示 HTTP 请求已成功构建并发送到服务器
HttpEventType.UploadProgress:提供有关上传进度的信息,包括已上传的字节和正在上传的文件的总大小
HttpEventType.ResponseHeaders:标记着从服务器接收响应头
HttpEventType.Response: 事件用于指示 HTTP 请求已经成功完成,并且服务器已发送响应数据
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。