简体   繁体   English

如何让 Observable Subject 返回同步数据

[英]How to make an Observable Subject return sync data

I am developing an image file upload code with Angular, with resizing and preview.我正在使用 Angular 开发一个带有调整大小和预览功能的图像文件上传代码。 The project has an HTML file input to select multiple images.该项目有一个 HTML 文件输入,用于选择多个图像。 Then, when onload, fire the upload function, resize and preview.然后,在加载时,触发上传功能,调整大小和预览。 Services upload and resize are called by subscribing.通过订阅调用服务上传和调整大小。 The issue is when I select several images to upload, the time to process resizing or something else causes an error because the subscribe is getting empty data (or late data...).问题是当我选择要上传的多张图像时,处理调整大小的时间或其他原因会导致错误,因为订阅正在获取空数据(或延迟数据......)。

 onFileSelected(event: any) { // Reset progress bar this.progress = 0; this.waiting = true; const selectedFiles = <FileList>event.srcElement.files; // Resize images first this.resizeService.resizeImage(selectedFiles, this.maxWidth, this.maxHeight) .pipe( take(1) ) .subscribe( (data: any) => { // Add property name to obj Blob for (let i = 0; i < selectedFiles.length; i++) { data[i].name = selectedFiles[i].name; } this.resizedFiles = data; this.waiting = false; // Do upload this.onUpload(); // Reset input file this.fileInput.nativeElement.value = ''; }); }

The resize service:调整大小服务:

 import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class resizeService { constructor() { } resizeImage(files: FileList, maxWidth: number, maxHeight: number): Observable<any> { let resizedFileSubject = new Subject(); let fileBlobArray: Blob[] = []; Array.from(files).map((file, i, { length }) => { let image: HTMLImageElement = new Image(); image.src = URL.createObjectURL(file); image.onload = () => { let width = image.width; let height = image.height; let canvas = document.createElement('canvas'); let ctx: any = canvas.getContext("2d"); let newHeight; let newWidth; const ratio = width / height; // Resize if needed if (width <= maxWidth && height <= maxHeight) { resizedFileSubject.next(file); } // Calculate aspect ratio if (width > height) { newWidth = maxHeight * ratio; newHeight = maxHeight; } else { newWidth = maxWidth * ratio; newHeight = maxWidth; } canvas.width = newWidth; canvas.height = newHeight; // Draw image on canvas ctx.drawImage(image, 0, 0, newWidth, newHeight); fileBlobArray.push(this.b64ToBlob(canvas.toDataURL("image/jpeg"))); // console.log('push: ', fileBlobArray) // Detect end of loop if (i + 1 === length) { // Wait to get async data - Workaround :( // setTimeout(() => { resizedFileSubject.next(fileBlobArray); // console.log('next: ', fileBlobArray) // }, 1000); } } }); return resizedFileSubject.asObservable(); } /** * Convert BASE64 to BLOB * @param base64Image Pass Base64 image data to convert into the BLOB */ private b64ToBlob(base64Image: string) { const parts = base64Image.split(';base64,'); const imageType = parts[0].split(':')[1]; const decodedData = window.atob(parts[1]); const uInt8Array = new Uint8Array(decodedData.length); for (let i = 0; i < decodedData.length; ++i) { uInt8Array[i] = decodedData.charCodeAt(i); } return new Blob([uInt8Array], { type: imageType }); } }

What I have to do is an workaround by adding a setTimeout of 1s to "wait" until everything is complete (sync an async call??).我必须做的是一种解决方法,即添加 1 秒的 setTimeout 以“等待”直到一切完成(同步异步调用??)。

The error occurs on here:错误发生在这里:

for (let i = 0; i < selectedFiles.length; i++) {
  data[i].name = selectedFiles[i].name;
}

Error:错误:

ERROR TypeError: Cannot set properties of undefined (setting 'name')
    at SafeSubscriber._next (app.component.ts:55)
    at SafeSubscriber.__tryOrUnsub (Subscriber.js:183)
    at SafeSubscriber.next (Subscriber.js:122)
    at Subscriber._next (Subscriber.js:72)
    at Subscriber.next (Subscriber.js:49)
    at TakeSubscriber._next (take.js:35)
    at TakeSubscriber.next (Subscriber.js:49)
    at Subject.next (Subject.js:39)
    at Image.image.onload [as __zone_symbol__ON_PROPERTYload] (resize.service.ts:56)
    at Image.wrapFn (zone.js:763)

Any help is appreciated.任何帮助表示赞赏。

Please note that you passed selectedFiles (a list) to resizeImage() function.请注意,您将selectedFiles (一个列表)传递给了resizeImage()函数。 you assumed that you will get a list (with the same size to selectedFiles ) of resized images in your subscription.您假设您将在订阅中获得调整大小的图像列表(与selectedFiles具有相同的大小)。 but you just next() one file in this line:但你只是next()这一行中的一个文件:

 if (width <= maxWidth && height <= maxHeight) {
          resizedFileSubject.next(file);
 }

that's why you got the error for data[i].name part here:这就是为什么您在此处收到data[i].name部分错误的原因:

   // Add property name to obj Blob
    for (let i = 0; i < selectedFiles.length; i++) {
      data[i].name = selectedFiles[i].name;
    }

I suggest pushing resized or not resized images in a separate array and next() the whole array at the end of resizing operation.我建议在单独的数组中推送调整大小或未调整大小的图像,并在调整大小操作结束时使用next()整个数组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM